「は」 制作 覺へ書き

ここには 自分が コーディングで つまづいたり 悩んだりしたことを含め 今後の制作の參考になるやう 解決策などを記したいと思つてゐる

テキストの挿入

もともとあるテキストの途中に 一文字挿入する という エディタでは當り前にある機能を實装するのに とても悩んだ
一回頭がぐちゃぐちゃになつて 中断し 好きなゲームとかをして 次の日の朝 じっくり取り組み
何とか 次のやうなコードを得た

addTxt :: Char -> State -> State
addTxt ch st = 
  let txs = lines (_tx st) 
      lns = lineStartX (_by st) txs 
      lnInfo = getLn 0 (_x st) lns
      lnNum = fst lnInfo
      newTxs = if lnNum>(-1)
        then
          let ln = txs !! lnNum
              txPos = getPosition (_by st) (_x st) (_y st)
                 (fst$snd lnInfo) (snd$snd lnInfo) ln 
              ltNum = fst txPos
              sp = take (snd txPos) (repeat ' ')
              newLn = (take ltNum ln)++sp++[ch]++(drop ltNum ln) 
           in (take lnNum txs)++[newLn]++(drop (lnNum+1) txs)
        else
          let blankLines = take (round (_x st - (last lns))) (repeat [])
              sp = take (round (_y st)) (repeat ' ')
           in txs++blankLines++[sp++[ch]]
      (nx,ny)= if (_y st)<(_by st) then (_x st,_y st+1) else (_x st+1,0)
   in st {_tx = unlines newTxs, _x = nx, _y = ny, _md=1}

lineStartX :: Float -> [[a]] -> [Float]
lineStartX by list = foldl (\acc ls -> acc++[(last acc +
              (if (length ls>0) then fromIntegral
                    (ceiling $ (fromIntegral (length ls))/(by+1))
                                else 1))]) [0] list 

getLn :: Int -> Float -> [Float] -> (Int,(Float,Float))
getLn _ _ (y:[]) = ((-1),(y,y+1))
getLn i x (y:z:ys) =
  if (x>=y && x<z) then (i,(y,z))
                   else getLn (i+1) x (z:ys)

getPosition :: Float -> Float -> Float -> Float -> Float -> [a] -> (Int,Int)
getPosition by x y x0 x1 ls =
  let lastLetters = fromIntegral (length ls) - (by+1)*(x-x0) 
   in if (x==(x1-1)) && lastLetters<y
          then (length ls,round (y-lastLetters))
          else (round ((by+1)*(x-x0)+y),0)

大まかな手順としては

  • 全體のテキストを一行づつに分ける (lines (_tx st))
  • 分けたものが 表示行として何行目から始まるのか示すリストをつくる (lineStartX)
  • カーソルのx位置に対応する文章(最初に分けたテキストの一行分)を特定し それが何行目と何行目の間にあるかを得る(getLn)
  • 上で カーソルのx位置が 表示されてゐる文の左側にあり どのテキストにも對應してゐない場合 (-1)が返されてゐる
  • 一文(テキストの一行分)が特定されてゐれば カーソルのx,yの位置に對應する 文章(Char型のリスト)の位置を特定する(getPosition)
  • 特定された場合 そこに挿入しやうとしてゐる文字を挿入し 全體のテキストを形作つていく
  • 文章の末尾から離れたところに カーソルのx, y座標があつた場合 そこまで空白や行を挿入し その後に文字を挿入して 全體のテキストをつくる

できたのは こんな感じのものである

www.nicovideo.jp

細かいエラーは 實行してみて直すのだが
とりあへずビルドに成功するのが目標だった
ひとつひとつ 考へやすい部分に問題を細分化していつて
簡單に實現できるところから 實装していくやうにした

まず 明確な目標をもつた
カーソルの位置を 複數行あるテキストの位置と對應させるには どうしたらよいか
これを實現しなければならない といふ 明確な目的意識である
これがある限り
「いつかは實現できる」
といふ確信が生まれる
もし そのときできなければ よく寝て 再び考へればよいのだ

とにかく 多くのことを 一度に意識して処理しやうとすると
頭が混亂して 「こんなん できるか!」
となる

「この目標を達成するために 一番簡單で すぐに出來ることは何か?」
といふのを 常に意識して取り組んだつもりだ

まう少し具体的に言ふと 仮に頭の中で 今カーソルが差し示してゐるx,y座標と テキストのリストの位置とを 同時に思ひ浮べてみる
なんかすごい大天才であれば それを処理して すぐに函數を書いたりできるのかもしれないが
少くとも 私はそんな 頭が良くはない
だから そんなふうに考へたら すぐに頭の中はオーバーヒートしてしまひ うへぇ~となつて 倒れてしまふ
だから 一度に多くのことを 処理しやうとしてはいけない

「まづ 文章のリストの何番目に 現在のカーソルが對應するのか それを特定することはできないか?」
これが とりあへずの目標となる
「そのためには 文章のリストが それぞれ 表示行の何行目から始まつてゐるのか 知らなければならない」
となる
ならば
「それを知るには どうしたらよいか」
これが 新しい目標となる

最初の目標など これをクリアするまで 忘れていいし 忘れた方がいい(でも 何か目標があった といふことだけは覺へておく)

ちなみに このコードを書くにあたつて 「空白行をどうするか」とか「空きスペースをどうするか」といふ問題は
全く後回しにした
正直 最初ビルドが成功したときには 「どうせ思つた通りには行かない筈」
と考へ 「問題が起きたところで コードを見て手直しすればいい」
くらゐに考へてをり 實際かなりうまく動いたので驚いたくらいだ

つまり コードの挙動の細部まで 全部理解できてゐなくても コードが一應エラーなくビルドできてしまへば
實行して結果を見てから対策を立てても良いのではないかと思ふ
これは もしかしたら 函數型コーディングの利點のひとつではないだらうか

常に變數の動きを意識しながら命令していかねばならないコーディングの場合
そのコードの挙動について完全に理解し 予測しなければ 實行時に必ずバグが出て
しかも そのバグの所在が分からない といつた事態になりさうだが
Haskellは さういふことが少ないのではないか と直感的に思ふ
まあ 私は命令型だらうが 函數型だらうが プロフェッショナルではないので 言ひ切ることは全くできないが

とにかく 目標が達成されたときの喜びは 何といふか 天にも昇る心地といふか
(そのまま昇天したら困るが)
とても嬉しい

しかし 道程はまだまだ長い
私にとつて ゴールは 恐れ多くて口で言へないほど 挑戰的である
どっかに書いたかもしれないが(笑)