Hatena::Grouphaskell

knenetのhaskell手記

2007-10-02

fizzbuzz 08:37 はてなブックマーク - fizzbuzz - knenetのhaskell手記

コードを書くのに慣れるために、FizzBuzz問題を解いてみる。

main = fizzBuzz [1..100]

fizzBuzz (x:xs) = do putFizzBuzz x
                     fizzBuzz xs
fizzBuzz [] = putStrLn "done."

putFizzBuzz x | mod x 15 == 0 = putStrLn "FizzBuzz"
              | mod x 5 == 0  = putStrLn "Buzz"
              | mod x 3 == 0  = putStrLn "Fizz"
              | True          = print x

解き始めてテストして動作を確かめるまで5分くらいだった。

もう少し時間をかければ、もっと良いコードになるだろうけど、FizzBuzzを詰めても仕方がないので問題を挙げるだけに留める。


FizzBuzz問題は、「繰り返し」と「分岐」と「異なる型の表示」の3つの問題を含んでいる。

haskellでは、一番ネックになるのは異なる型を扱い、それを表示することではないだろうか。

今回は、表示する段で条件分岐して、それぞれの値に対する表示を行った。

他には、新しい型を作って、それでFizzBuzzリストを作るとか。そうすれば表示側は分岐しなくて済む。

副作用の分離 08:37 はてなブックマーク - 副作用の分離 - knenetのhaskell手記

当たり前かも知れないけど、リアルタイムに入力を得て結果を返す関数は、入力を得る部分と、計算する部分は分離するべきである。

そうでないと、計算する部分に任意の値を与えてテストをすることがしにくくなる。

さらに、表示する部分も分けられた方が良い。テスト用には、実際の動作より、数値のリストなどの方が分かりやすいかも知れない。

このように分離をするためには、別に変数を使う必要はない。

それぞれの関数を別々に作り、呼び出し側で、それらをパイプした関数を作ればいいのだ。

それがMonadでありArrowである(と思う)。


インタラクティブなループするプログラムを作るには、

  • 入力を受ける
  • 前回のデータを引き継ぐ
  • 新しいデータを用いて表示系を書き直す

必要がある。

入力があるまで動かないイベントドリブンと、入力がないことも入力の一つとみなすタイムドリブンがある。

どちらにせよ、入力が決まったら次の動作に移行できる。

それさえ出来れば、現在の必要なデータを全て引数にして、次の動作へ写す関数を考えれば良い。

MonadでやるのとArrowでやるのとどちらの方が上手く書けるだろうか……。

haskellの動作イメージ 17:28 はてなブックマーク - haskellの動作イメージ - knenetのhaskell手記

どういう構造になっているか、出来るだけ単純に表すというのは、厳密にやらないとあまり意味がないけど、そういうのをぼんやり考えるのが好きだ。


haskellは、大雑把に見れば値の定義しかできない。そして、定義には順番は関係ない。

ただ、それだけだと何を表しているかさっぱり分からないので、値の定義がどこに所属するかを書ける。

実際、値の宣言と包含関係の提示が出来れば、データ構造を表すのには十分である。


データ構造が表せるだけではプログラミング言語としては不十分だ。ユーザが要求した値を返さなくてはいけない。

そのために、他の値の羅列で定義された値を評価して簡約する。簡約化の順番は、やはり定義によって決められる。


インタプリタなら、ユーザが指定する値をただ返せばいい。だが、普通のプログラムはそうなっていない。

ユーザは一つの値しか要求できず、しかもその値は破棄してしまう。

もはや、ただ値を返すプログラムは何のためにあるか分からない。

そこで登場するのが副作用である。


haskell副作用を作るために少し細工がしてある。値を簡約化する際にアクションを起こせる。

アクションが起きると、データが書き換えられて世界が変わる。これはプログラムにとっては不都合だが、ユーザはそういった反応がなくては困ってしまう。


haskellプログラムはこんなイメージだろうか。

これを書いていたら、アスピレータっぽいと思った。水を入力して、水がそのまま出力される。この水は捨ててしまうけど、枝管の先が減圧されるという副作用が欲しい。

メインの作用は分かりやすい。でも、実際に使われるのは副作用で、それがどうやって引き起こされるのかは理解しにくい。


自作のスクリプト

haskellはデータ構造を表すのが基本だ。LISPも大体同じである。だとすれば、これらの書式はお絵描きツールのスクリプトにも流用できる。実際に絵を描くプログラムは作れるし。

しかし、そういった使い方をするには書式が一般的すぎる。haskellのコードでは絵を描いていることが一見して分かるとは思えない。

そこで、コードの書式を参考にしてスクリプトのルールを考える。やはり、プログラマは自分の言語を作れるべきなのだ。

そのためにも、良くできた言語を習得することは役に立つ。

マシン語やCの学習は、機械の動作を覚えるのには役に立つだろうが、スクリプトを作るためには役に立たないだろう。現在どちらの方が有用かというと、やはりスクリプトの作成に分があるだろう。