ep19122
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 卒業研究(第1~5回) ###### tags: `研究` `卒業研究` `卒研` `DnD` `UI` `Undo` `Redo` # B1. プログラムエディタの改良 <!-- Markdown記法について:https://qiita.com/Qiita/items/c686397e4a0f4f11683d --> <!-- 表テーブル:https://notepm.jp/help/markdown-table --> <!-- --> # メンバー * 寺田 EP19071 * 清水 EP19122 # 第1回(4/19(火)) - 計画建て ## -全体目標- 「DnD機能を向上, UIを改良して機能の向上を目指す」 ※「DnD」とは、「ドラッグ&ドロップ」のことを指す。 ## -卒研計画- * アイディアを出しを二人で行う。 * DnDを寺田、UI改良を清水が担当する。 * 資料作成(第一回は共通発表用)を行う。 * 春学期までに分担作業(DnD, UIの改良)を終わらせ、 秋学期に合体させるという大雑把な計画がある。 →作業が完了次第、相方の作業の手伝いを開始する予定もある。 ## -計画の問題点・改善点- * UIの向上の定義が曖昧。 →Scratchを参考にUIの改善案を決めていこうという草案。 →Scratchにはないものを足していくのが今後の方針。 * 現状先行実験で使ったプログラムがないから具体案を出せない。 ~~→Googleドライブのリクエスト承認待ち。~~ →リクエストが通っため、解決 # 第2回(4/26(火)) - エディタ改善の提案・環境整備・コード理解 ## -第2回の目標- ①「ScratchとCU-Bricksと比較し、CU-Bricksの不足部分を把握・実装検討する」 ②「Elmのコードを編集し、JavaScript(以降JS)にコンパイルできる環境を作る」 ③「先行実験のブロックエディタのELMコードを理解する」 * ③は、先行実験の活動記録を確認した際、4月中にELMコードを理解しようとしていたため、 その行動を模倣してみることにした。 ## -①について実施したこと- ### CU-Bricksが、Scratchと比べて不足している点 * セーブ機能がない(デスクトップへデータを保存できない) * ↑の機能に合わせて、ロード機能がない(デスクトップからデータを読み込めない) * 作業の取り消し(Undo)機能がない * 上の機能に合わせて、取り消しの取り消し(Redo)機能がない * 変数が存在しない * ブロックや、その塊に対するコメント機能がない ### 実現できそうな機能(UIの改善案) * セーブ・ロード機能は、抽象構文木(AST)を文字列として保存し、 それを数値として読み込める機構を作れば、実装が可能な感じはする。 * Undo・Redo機能は、セーブ・ロード機能を実装できれば、それと似た仕組みで 一時記憶させることで、実装可能な感じはする。 * 変数は、ブロックの記録がある抽象構文木(AST)を、Elmでどのように記述されているかを 理解すれば、ブロックに紐づいたASTを新たに追加する形で実装できそうな感じはする。 ### 逆に、実装できなさそうな機能 * 右クリックでブロックを複製する仕様なため、コメントを追加する機能が追加できなさそう。 ## -②について実施したこと- ### 環境作り 寺田がゼミBにて作っていた開発環境と同様の方法で、 先行実験のElmコードが開発されていたため、清水も同様の環境を整備することにした。 [このインストール作業解説ページ](https://guide.elm-lang.jp/install/elm.html)を参照した。 ### 結果 寺田、清水、ともに開発環境は作れた。 後は、必要に応じて、パッケージをインストールだけである。 (少なからずとも、2022/4/25現在では、パッケージも最低限必要な分だけ導入済み) | 現在導入中のパッケージ名 | | :----: | | elm/browser | | elm/core | | elm/html | | elm/http | | elm/json | | elm/svg | | elm-community/json-extra | ※[このサイト](https://package.elm-lang.org/)にて、パッケージの詳細を調べられる。 ## -③について実施したこと- ### Elmコードで現状分かっていること | 項目(内容) | 開始行数 | | :----: | :----: | | ファイル名 | 1 | | import | 3 | | MAIN | 24 | | SUBSCRIPTIONS | 35 | | UPDATE | 251 | | VIEW | 678 | * VIEW内にDecoderがある。 ### Elmコードの内、現状理解できていないところ * SUBSCRIPTIONSとは何かを理解できていない。 →[ここ](https://guide.elm-lang.jp/effects/)でサブスクリプションの概要は分かったが、  結局何なのかの実態は掴めていない。 ## 総括 ### 目標に対して達成したこと * Slackを通じた連絡・相談が円滑に進まなかったため、UI改善の提案があまり進んでいない。 * 開発環境の整備は完了した。 * Elmのコードの理解は、大枠を軽く理解する程度で終わってしまっている。 ### 反省点 * お互いに、就活やコロナワクチン(副反応含め)により、Slackを通じた連絡や相談を あまり上手く行えてなかったため、半分程度の達成結果になってしまった。 →今週の目標のいくつかを、来週分にまわす必要がある。 * また、Elmのコードの理解をもっと深めないと、DnDおよびUI改善の作業に 取り掛かれないので、次週は、より広く、深く理解できるようにする必要がある。 # 第3回(5/10(火)) - 変数提案・DnD提案① ## 前回(第2回)に行ったこと * Scratchを参考に、UIの改善案を出した。 * ElmコードをJavaScriptコードにコンパイルする環境を整えた。 * 先行実験のElmコードの理解に努めた(本当の大雑把な概要部分のみ)。 ## 前回をもとに、今回やるべきこととして決めた内容 * プログラム言語として不足している部分を洗い出す * 変数についてもっと具体的に考える * ドラッグ&ドロップについて、そろそろ具体的に考えてみる * コードの理解だけは、奥居先生との都合が合わず、次週(第4回)以降に回すこととなった。 ___→今回の発表終わりらへんで相談します。___ ## -第3回の目標- ①「紙にでも、実際にプログラムを作ってみて、不足していると考えられる機能を洗い出す」 ②「変数実装について、もっと具体的に考える」 ③「DnDの改善案を考える」 ## -①について実施したこと- ### 不足している機能 Scratchだけでなく、Google Blocklyも参考に、不足している要素を考えてみた。 尚、ソフトウェア開発前提に考えており、toio等のマイコンを操作するための 機能は前提として考えていない。 →マイコンに作成したコードを送る機能が、そもそも先行研究では無かったため。  考慮することを必要以上に増やしすぎてはいけない。 * 関数定義 * 乱数 * コンテキストメニュー(ショートカットメニュー)での、選択肢 →ブロックを右クリックで、複製・削除・コメント追加の3種類の選択肢が表示される * If文 ### 実装してもよさそうな機能 * 関数定義 →whileやforなしに、指定した通りに繰り返す動作を実装するため、関数の実装が必要である。 * 乱数 →「じゃんけん」など、ランダム性のあるプログラムの作成には必須だろう。 * コンテキストメニュー →現状だと、右クリックの機能が複製しかないため、コメント追加やブロック削除機能を  ここにまとめても、問題ないであろう。 ### 逆に必要ないと判断できる機能 * If文 →CaseブロックがIf文に相当するため、実装の必要性はなかった。 ### 前回(第2回)の案も含めたイメージ図 ![](https://i.imgur.com/RNB7q6a.jpg) * 関数を「とぶ」と「くる」のブロックで表現。 * 本来文字を打ち込めるところに、変数ブロック(ランダムも含む)を埋め込もうと考えている。 * ランダムブロックは、最小値と最大値を指定できるようにする。 ## -②について実施したこと- ### 一番大きな問題点 まずCU-Bricksの対象が、小学生か中学生以上かにより、変数の説明(仕様)が異なってくる。 * 小学生→算数にて、式内のわからない部分を〇, □, △等で置き換える計算方法を習っている     筈なので、その性質に準拠した変数が、理解されやすい可能性がある。     しかし、それは数学の変数を教えてるだけであるという別の問題が出現する。 * 中学生→数学にて、x, y等の変数を数学で習っている筈なので、数学の変数と     プログラムの変数の違いを説明すれば、Scratchと同じような変数の入力方法でも     問題ない可能性がある。 上記のように、対象により、 ___CU-Bricksの変数を、数学の変数として扱うべきか、 プログラムの変数として扱うべきか___ という問題がある。 ### それ以外の細かい問題点 * ローカル変数とグローバル変数を区別する余裕が、対象者にはない可能性がある。 →プログラムの変数と、数学の変数の違いを区別するので精一杯な可能性があるため。 * 更に、プログラミング学習という側面を考えると、簡潔なプログラムを自然と書ける 環境である必要がある。 →ローカル変数のような、同じxでも、扱う数値が違う環境を用意すると、  不必要に変数が増える可能性があるため、グローバル変数を用いて、  使える変数を減らしたほうが良い可能性がある。 ### これらの問題点を総合的に考え、変数の在り方を決めた。 * 小学生向け前提で考えたほうが後で修正が効くので、小学生向けとして考えてみる。 →中学生以上向けとして考えると、変更したときに問題点が増えてしまうため。 * xやyの文字を使うと、算数が苦手な子どもが受け入れられない可能性を考慮し、 〇, △, ▢, ✕,☆の5種類の全角記号を変数の代わりとして使用する案が出た。 →変数の種類を制限してやることにより、自然と簡潔なプログラムになるようにする。 →同じ〇でも入っている数値が違うという直感的に理解できない要素を省くため、  グローバル変数として、5種類の全角記号を用いることにする。 * また、数学的な変数ではなく、プログラム的な変数を用いることにした。 →あくまでも、プログラミング学習が目的なので。 →「=」が説明する必要性も含めて厄介なので、「〇ずつ増やす」などの文章で表現する。 * 変数の種類を5つのみにするという制約を課してはいるが、種類数にこだわりはなく、 単純に区別しやすい全角記号がこれくらいしかなかっただけである。 明らかに足りないなら追加するまでである。 ## -③について実施したこと- ### DnDの改善案と結果 * offsetX/Yではなく、clientX/Yを用いた方法。 → clientX/Yに切り替えただけだと、ポインタに追従しきれないとき、  図形がマウスに追従しなかったので、解決しなかった。 * divを多重構造にして、図形外にonMouseUp, onMouseMoveを置き、 図形内にonMouseDownを置く方法。(先行実験通り、offsetX/Yを採用) → 図形がポインタに追従しきれないとき、図形の座標移動が激しくなっていたため、  これも解決しなかった。 * clientX/Yを用いたうえで、divを多重構成にした方法。 → DnD中にポインタが図形から外れた時、図形がマウスに追従していたため、  これを採用することにする。 ### 実際に作成した簡易的なElmコード * clientX/Yを用いたElmコード ``` module DnDTest1 exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode as JD exposing (map2, field, int, succeed, Decoder) main = Browser.sandbox { init = initialModel, update = update, view = view } type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ initialModel : Model initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化 type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」 update : Msg -> Model -> Model update msg model = case msg of Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my } Up -> { model | flag = False, color = "#5499e0" } Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" } view : Model -> Html Msg view model = div [ style "position" "absolute", style "background" <| model.color , style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx , style "width" "250", style "height" "250" , onMouseDown Down , onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作 , if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する else onMouseUp Up ] [ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示 movementDecoder : (Int -> Int -> msg) -> Decoder msg movementDecoder g = JD.map2 g (JD.field "clientX" JD.int)(JD.field "clientY" JD.int) onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する onMouseMove h = on "mousemove" <| movementDecoder h onMouseDown i = on "mousedown" <| movementDecoder i onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する ``` * divの多重構造のElmコード ``` module DnDTest2 exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode as JD exposing (map2, field, int, succeed, Decoder) main = Browser.sandbox { init = initialModel, update = update, view = view } type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ initialModel : Model initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化 type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」 update : Msg -> Model -> Model update msg model = case msg of Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy} Up -> { model | flag = False, color = "#5499e0" } Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" } view : Model -> Html Msg view model = div [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている , onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作 , if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須) ] [ div [ style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言 , style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新 , style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定 , onMouseDown Down -- (図形内)クリックを「した瞬間」の動作 ] [ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示 ] movementDecoder : (Int -> Int -> msg) -> Decoder msg movementDecoder g = JD.map2 g (JD.field "offsetX" JD.int)(JD.field "offsetY" JD.int) onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する onMouseMove h = on "mousemove" <| movementDecoder h onMouseDown i = on "mousedown" <| movementDecoder i onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する ``` * clientX/Yと、divの多重構造のElmコード ``` module DnDTest3 exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode as JD exposing (map2, field, int, succeed, Decoder) main = Browser.sandbox { init = initialModel, update = update, view = view } type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ initialModel : Model initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化 type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」 update : Msg -> Model -> Model update msg model = case msg of Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my } Up -> { model | flag = False, color = "#5499e0" } Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" } view : Model -> Html Msg view model = div [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている , onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作 , if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須) ] [ div [ style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言 , style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新 , style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定 , onMouseDown Down -- (図形内)クリックを「した瞬間」の動作 ] [ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示 ] movementDecoder : (Int -> Int -> msg) -> Decoder msg movementDecoder g = JD.map2 g (JD.field "clientX" JD.int)(JD.field "clientY" JD.int) onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する onMouseMove h = on "mousemove" <| movementDecoder h onMouseDown i = on "mousedown" <| movementDecoder i onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する ``` ### 問題点 * 実際の先行実験のElmコードに組み込んで確かめないといけない。 →コードを理解しきれていないので、どのように組み込めばよいかが判断できない。 ___→奥居先生と共に、先行研究で作成されたElmコードの理解を行う機会を頂きたいです。___ ## 総括 ### 目標に対して達成したこと ①は、プログラミング言語的な不足部分だけでなく、エディタ的な不足部分も提案できたため、 達成できたと判断する。 ②は、変数を、グローバル変数として扱い、〇や△などの全角記号で直感的に分かりやすくする という、具体的な提案ができているため、達成できたと判断する。 ③は、簡単なElmコードを作成して検討することはできたが、実際に先行研究のコードを改善 できている訳ではないため、確証が持てない段階であるとし、半分達成できたと判断する。 ### 反省点 * 連絡・成果共に反省点はなかった。 * コードの理解がまだできていないため、結局提案で終わってしまっている。 ### 次回の目標 * コードの理解を行う(改善案を組み込む方法の目安がつく程度には) * DnDとUI改善の作業をそれぞれで行い始める。 * 作業の過程で、GitHubをきちんと使うようにする。 # 第4回(5/17(火)) - DnD提案②・関数の引数 ## 前回(第3回)に行ったこと * プログラム言語的・エディタ的に足りない機能を考え、実装イメージ図を作成した。 * 変数はグローバルで、全角記号(5種類)のみ、=や>等の数学記号をなるべく使わないよう決定。 * DnDについて、offsetX/Yでなく、clientX/Yのdivの多重構造にしようと草案した。 ## 前回をもとに、今回やるべきこととして決めた内容 * 関数に渡す変数の引数について、もっと議論すべきである。 * DnDについて、pageX/Y, screenX/Yについても、議論すべきである。 * 動的・静的スコープの概念から、関数の引数を考えるとよい。 * 右クリックでブロック複製できる利便性を何かしらの形で残すべきである(議論の余地あり)。 ## -第4回の目標- ①「DnDの実装案について、pageX/Y, screenX/Yも検討して考える」 ②「関数の引数について、具体的に考える」 ## -①について実施したこと- ### pageX/Y, screenX/Y, clientX/Yをそれぞれ比較した(div多重構造前提で) ※あくまでも、Google Chromeブラウザで動作させている。 * pageX/Y clientX/Yと動作に変化はなかった(縮小表示でも)。 clientX/Yは、ブラウザの左上を原点に考えるが、 pageX/Yの場合は、コンテンツの左上を原点に考えているため、 スクロールするコンテンツには、こちらが向いていそうではある。 また、[ブラウザの相互性が高い(JSの場合)](https://developer.mozilla.org/ja/docs/Web/API/MouseEvent/pageX)。 * screenX/Y clientX/Yと動作に変化はなかった(縮小表示でも)。 screenX/Yは、ディスプレイの左上を原点に考えるため、 モニタの画質に合わせたサイズ調整されたものを作る場合、 比率の違いから、座標がずれる可能性が高いため、選択肢には入らない。 また、[ブラウザの相互性が高い(JSの場合)](https://developer.mozilla.org/ja/docs/Web/API/MouseEvent/screenX)。 ### 結論 相対座標(ブラウザの左上)よりも、絶対座標(ページの左上)の方が、 後々必要になった際に便利と考え、pageX/Yを採用する。 ### 現状の懸念点 先行研究のエディタを確認すると、ブロックがあるパレット部分と、プログラムを作る部分で、 ページのサイズが異なる可能性がある(それぞれ独立したスクロールバーがあるから)。 →pageX/Yで座標取得したときの動作が不明(実装しないと判明しない点である)。 ### 検証のために使用したElmのコード * pageX/Y ``` module DnDTest4 exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode as JD exposing (map2, field, int, succeed, Decoder) main = Browser.sandbox { init = initialModel, update = update, view = view } type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ initialModel : Model initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化 type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」 update : Msg -> Model -> Model update msg model = case msg of Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my } Up -> { model | flag = False, color = "#5499e0" } Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" } view : Model -> Html Msg view model = div [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている , onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作 , if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須) ] [ div [ style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言 , style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新 , style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定 , onMouseDown Down -- (図形内)クリックを「した瞬間」の動作 ] [ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示 ] movementDecoder : (Int -> Int -> msg) -> Decoder msg -- Decoder(movementX, movementY) movementDecoder g = JD.map2 g (JD.field "pageX" JD.int)(JD.field "pageY" JD.int) onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する onMouseMove h = on "mousemove" <| movementDecoder h onMouseDown i = on "mousedown" <| movementDecoder i onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する ``` * screenX/Y ``` module DnDTest5 exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode as JD exposing (map2, field, int, succeed, Decoder) main = Browser.sandbox { init = initialModel, update = update, view = view } type alias Model = { hx: Int, hy: Int, zx: Int, zy: Int, color: String, flag: Bool } -- (zx, zy)は現在の図形の左上の座標, (hx, hy)はマウスダウン時のマウス座標、colorはRGB, flagはクリックされたかどうかのフラグ initialModel : Model initialModel = { hx = 0, hy = 0, zx = 100, zy = 100, color = "#5499e0", flag = False } -- Model内の名称別の数値の初期化 type Msg = Move Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Down Int Int -- Int型の変数二つをメッセージとして(イベントの値を)受け取る | Up -- JD.succeedにより、Int(2つ)を引数として渡して「いない」 update : Msg -> Model -> Model update msg model = case msg of Move mx my -> { model | zx = model.zx + mx - model.hx, zy = model.zy + my - model.hy, hx = mx, hy = my } Up -> { model | flag = False, color = "#5499e0" } Down x y -> { model | hx = x, hy = y, flag = True, color = "#95c0ec" } view : Model -> Html Msg view model = div [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている , onMouseUp Up -- (図形外)マウスを「押した」ときと「離した」の動作 , if model.flag == True then onMouseMove Move -- (図形外)「クリック中」のときのみ、マウスの移動方向と大きさ(ベクトル)を取得する else onMouseUp Up -- (図形外)クリックしてないときに、ドラッグ移動したと判定するわけにはいかないので、特に値を変更させない「Up」を使う(elseはifを使う際必須) ] [ div [ style "position" "absolute", style "background" <| model.color -- 図形の座標を絶対座標で指定することと、背景の色の指定を宣言 , style "top" <| String.fromInt model.zy, style "left" <| String.fromInt model.zx -- 図形の左上の座標を更新 , style "width" "250", style "height" "250" -- 図形の横幅と縦幅を決定 , onMouseDown Down -- (図形内)クリックを「した瞬間」の動作 ] [ div [][ text ("現在の図形座標(" ++ (String.fromInt model.zx) ++ ", " ++ String.fromInt model.zy ++ ")") ] ] -- 現在の図形の左上の座標表示 ] movementDecoder : (Int -> Int -> msg) -> Decoder msg -- Decoder(movementX, movementY) movementDecoder g = JD.map2 g (JD.field "screenX" JD.int)(JD.field "screenY" JD.int) onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg -- マウスが「移動中」のときのみ動作する onMouseMove h = on "mousemove" <| movementDecoder h onMouseDown i = on "mousedown" <| movementDecoder i onMouseUp j = on "mouseup" <| JD.succeed j -- クリックを「離した瞬間」のみ動作する ``` ## -②について実施したこと- ### 変数の一般的な在り方から、メリット・デメリットを考える * グローバル変数を用いる場合 同じ全角記号の場合、すべて保存(変更)する数値は統一化できるメリットはある(簡易的)。 ただし、この場合、関数に引数を渡す意味がない。 (関数内外で変数の数値は統一化されるため) * ローカル変数を用いる場合 AチームとBチームで、同じ全角記号でも、違う値を保存できるメリットはある。 また、関数内外でも同じ全角記号で別の数値を保存できる。 ただし、簡易的(簡潔的)なコードを作成するのには向いていない。 また、小学生や中学生にこの概念を理解できるのかという問題あり。 更に、せっかくビジュアルで直感的に理解できるのに、同じ記号が書かれているブロックが 並んだ際、その違いが良くわからなくなる問題がある。 * スタックの値を用いる場合 関数のコードを動作させている間のみの変数の数値変動が起きるため、 一時的な計算用に変数を用いる場合はこれが有利に働く。 ただし、関数で計算した内容を、関数外に持って来れない。 また、関数内外で、それぞれ変数を宣言する必要性が出てくる。 ### 結論 グローバル変数を用いることに決めた(下記に理由)。 * ビジュアル的に分かりやすい。 * 関数で計算した内容を、関数外に引き継げる。 * 変数宣言が不要となる(変数の数にあらかじめ制限を設けているため)。 * エディタ側で変数の現在の数値を表示させる場合、これが一番見やすい。 ## 総括 ### 目標に対して達成したこと ①は、ブラウザとの相互性と、縮小・最大表示の動作確認を行った上で決めたため、 達成できたと判断する。 ②は、メリット・デメリットと、CU-Bricksの在り方を比較して決めたため、達成できたと判断する。 ### 振り返り * 連絡は取れた。 * 先週と今週の成果をはっきりさせている。 * 骨組みだけでなく、中身も深く考慮して考えたため、制作イメージが掴めた。 ### 次回の目標 * 引き続き、先行実験のコード理解を行う。 * エディタの機能追加案をもとに、簡易的なElmコードを作成してみる。 * DnDの改善案を、実際に先行研究のElmコードに組み込んでみる。 # 第5回(5/24(火)) - 論文読み・DnD改善案設計① ## 前回(第4回)に行ったこと * DnDは、ScreenX/Y(ページの一番左上が原点)を使って、マウスの座標を取得することに決まった。 * 変数はグローバル変数を用いて、関数には引数を用いらないことに決めた。 →これは、ビジュアル的な分かり易さと、CU-Bricksの対象者を想定して決めた。 ## 前回をもとに、今回やるべきこととして決めた内容 * DnDの改善案を、実際の先行研究のElmコードで実装可能か実際に試してみる。 →そのために、先行研究のElmコードの理解が大事である。 * エディタの機能追加案をもとに、簡易的なElmコードを作成してみる。 →先行研究のElmコードに、実際に追加するわけではない。 →今週は、DnDとコードレビューへの事前勉強に時間を費やしたため、これはできなかった。  恐らく来週はDnD改善を2人で行う可能性が高い(第5回・「振り返り」項目参照)。 ## -第5回の目標- ①「先行研究のElmコードを、過去の論文から理解を図る」 ②「DnDの改善案を、実際の先行研究のElmコードに組み込む」 ## -①について実施したこと- ### Elmの言語使用的に理解したこと * Elmの外(JSなど)向きのメッセージ(Cmd)、内向きのメッセージ(Sub) →[こちらを参照](https://guide.elm-lang.jp/interop/ports.html) * Subscriptionsを用いて、時間など、外部から来るメッセージを定期的に待ち受けることが可能 →[こちらを参照](https://guide.elm-lang.jp/effects/time.html) ### 結果 Elmの関数と、JavaScriptのナビゲーションバーとポートについての概要理解を図ることができた。 ## -②について実施したこと- ### まず、「pageX/Y」と「divの多重構造」を組み込めそうか? * 「offsetX/Y」を「pageX/Y」に変更すること自体は可能そうである。 →どちらも、「ブロックの新x(y)座標= x(y) + x1(y1) - x0(y0)」で求まるから(先行研究参照)。 * しかし、divの多重構造が実装可能かどうかが怪しい。 →パレットとブロック操作の同時描写「view」、パレット描写「viewPallet」、  根ブロック描写「viewASTxy」、 根以外の木の再帰的描画「viewAST」等、 多くのview関数が混在しているため。 ### デバッグ用に、Ellieに先行研究のコードを書いておいた https://ellie-app.com/hzjbPxX8nFLa1 ### 実際に作成してみた #### 追加する内容 * 根のブロックの描画「viewASTxy」にて、「mouseup」と「mousemove」があったため、 ここをdivの多重構造にする。 →下記に、そのコードと、そのときに表示されたエラーのSSを示す ``` -- 根のブロックの描画 viewASTxy : Model -> ASTxy Node -> Html Msg viewASTxy model (ASTxy (x, y) (ASTne n b r)) = Keyed.node "div" [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている -- mouseup , on "mouseup" <| whenDragging model <| whenLeftButtonIsDown <| Decode.succeed <| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r)) -- mousemove -- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要 , preventDefaultOn "mousemove" <| whenDragging model <| let ( x0, y0 ) = model.getDnDInfo.getXY0 in Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 )) |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float) ] [ ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで , ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r ) , ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b ) , Keyed.node "div"[ style "position" "absolute" , style "top" (String.fromFloat y ++ "px") , style "left" (String.fromFloat x ++ "px") -- mousedown , on "mousedown" <| whenNotDragging model <| whenLeftButtonIsDown -- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット (Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加 |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float)) -- contextmenu -- コンテクストメニューが開かないようにpreventDefaultが必要 , preventDefaultOn "contextmenu" <| whenNotDragging model <| whenRightButtonIsDown <| Decode.succeed <| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r)) ] [ ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで , ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r ) , ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b ) ] ] ``` ![](https://i.imgur.com/MZa5C2R.png) ### エラーについて > This `node` call produces: Html Msg But all the previous elements in the list are: ( String, Html Msg ) Hint: Everything in a list must be the same type of value. This way, we never run into unexpected values partway through a List.map, List.foldl, etc. Read <https://elm-lang.org/0.19.1/custom-types> to learn how to 窶徇ix窶・types. * 「HTML Msg」を呼び出したいが、実際には「String, Html Msg」が呼び出されているから、 宣言したモノと、記述したモノの型が一致しない現象が起きている。 * 「カスタム型の解説ページを参照してくれ」といったHintがついている。 ### 別の方法でアプローチしてみた #### 内容 * 多重構造になっているdivのうち、外側は「Keyed.node "div"」、内側は「div」を使ってみる方法。 #### コードとエラー ``` -- 根のブロックの描画 viewASTxy : Model -> ASTxy Node -> Html Msg viewASTxy model (ASTxy (x, y) (ASTne n b r)) = Keyed.node "div" [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている -- mouseup , on "mouseup" <| whenDragging model <| whenLeftButtonIsDown <| Decode.succeed <| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r)) -- mousemove -- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要 , preventDefaultOn "mousemove" <| whenDragging model <| let ( x0, y0 ) = model.getDnDInfo.getXY0 in Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 )) |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float) -- contextmenu -- コンテクストメニューが開かないようにpreventDefaultが必要 , preventDefaultOn "contextmenu" <| whenNotDragging model <| whenRightButtonIsDown <| Decode.succeed <| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r)) ] [ div[ style "position" "absolute" , style "top" (String.fromFloat y ++ "px") , style "left" (String.fromFloat x ++ "px") -- mousedown , on "mousedown" <| whenNotDragging model <| whenLeftButtonIsDown -- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット (Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加 |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float)) ] [] , ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで , ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r ) , ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b ) ] ``` ![](https://i.imgur.com/NBqz0NG.png) #### エラーについて > The 2nd element is a tuple of type: > > ( String, Html Msg ) > > But all the previous elements in the list are: > > Html Msg > > Hint: Everything in a list must be the same type of value. This way, we never > run into unexpected values partway through a List.map, List.foldl, etc. Read > <https://elm-lang.org/0.19.1/custom-types> to learn how to 窶徇ix窶・types. * 810行目の二番目の要素は、「String, Html Msg」だが、 リスト内の要素が「Html Msg」のみになってしまっている。 ### 結論 * 型の違いによるエラーは、別アプローチで解決されたように見える。 * しかし、リストの型が正常でなくなってしまっているように思われる。 * よって、このDnDの改善は、単にdivを多重構造にすれば解決する問題ではないことが分かった。 →むしろ、型によるエラーが発生してしまっている。 ### 追記 #### divの構造をちょっと変更してみた * 今までは、下記のようなdivの多重構造をしていた ``` -- 根のブロックの描画 viewASTxy : Model -> ASTxy Node -> Html Msg viewASTxy model (ASTxy (x, y) (ASTne n b r)) = Keyed.node "div"[ -- 省略 -- ] [ div[ -- 省略 -- ] [ ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで , ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r ) , ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b ) ] ] ``` * しかし、下記のように、「div」と「Keyed.node "div"」を入れ替えたところ、 コンパイルエラーが発生しなかった。 →メッセージに渡される型と、ノード中の命令に記述されている型を一致するかもしれない  という打算的な理由で試した。 ``` -- 根のブロックの描画 viewASTxy : Model -> ASTxy Node -> Html Msg viewASTxy model (ASTxy (x, y) (ASTne n b r)) = div [ style "width" "100%", style "height" "100%" -- 見えない背景用図形を用意することで、動く図形の外でもDown、Move、Upが機能するようにしている -- mouseup , on "mouseup" <| whenDragging model <| whenLeftButtonIsDown <| Decode.succeed <| MsgAttachMe (ASTxy ( x, y ) (ASTne n b r)) -- mousemove -- ブロック表面の画像だけがドラッグされないようにpreventDefaultが必要 , preventDefaultOn "mousemove" <| whenDragging model <| let ( x0, y0 ) = model.getDnDInfo.getXY0 in Decode.succeed (\x1 y1 -> MsgMoveUs ( x, y ) ( x1 - x0, y1 - y0 )) |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float) ] [ Keyed.node "div"[ style "position" "absolute" , style "top" (String.fromFloat y ++ "px") , style "left" (String.fromFloat x ++ "px") -- mousedown , on "mousedown" <| whenNotDragging model <| whenLeftButtonIsDown -- MsgLetMeRootは根には無意味、代わりにMsgStartPUDnDを単独でセット (Decode.succeed (\x1 y1 -> MsgStartPUDnD ( x, y ) ( x1, y1 )) --( x, y ) を追加 |> Decode.andMap (Decode.field "offsetX" Decode.float) |> Decode.andMap (Decode.field "offsetY" Decode.float)) -- contextmenu -- コンテクストメニューが開かないようにpreventDefaultが必要 , preventDefaultOn "contextmenu" <| whenNotDragging model <| whenRightButtonIsDown <| Decode.succeed <| MsgCloneUs (ASTxy ( x, y ) (ASTne n b r)) ] [ ("N", lazy3 viewBrick model ( x, y ) n ) -- 実際のブロックの描画はviewBrickで , ("R", lazy4 viewAST model ( x + interval model, y ) ToRight r ) , ("B", lazy4 viewAST model (x, y + interval model ) ToBottom b ) ] ] ``` #### 動作結果 [一応Ellie版あり](https://ellie-app.com/hzCHchGh9Tfa1) * 白背景が新たに生成され、パレット部分が下に追いやられてしまった。 * また、divの多重構造にしても、DnDがスムーズにならなかった。 →これにより、divの多重構造によるDnDのスムーズ化ができない可能性が出てきてしまった。 ## 総括 ### 目標に対して達成したこと ①は、それぞれの関数の概要理解に利用できる範囲まで引用と説明を加えられたため、達成できたと判断する。 ②は、実際にコードに記述してみる行動はできたが、エラーの原因が完全に理解できたわけではないため、半分達成したと判断する。 ### 振り返り * 先行研究の内容理解を図れた(完全に理解できたかはともかく) * DnD改善を行うアプローチはできた * エラーの原因を正直あまり理解できなかった * 白背景が生成されたことにより、パレットが下に追いやられる予想はできなかった。 * 1週間あたりの卒研に充てれる時間をもっと確保する必要性がでてきた。 ### 次回の目標 * DnDの改善作業を2人で行う。 →これは、DnD改善作業が、想像以上に重労働になりそうなため。 * コードレビューへの事前勉強と、復習を行う。 →今の段階でも勉強はしているが、少しでも多くの事前知識を持っておくのは大事であろう。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully