プログラミングHaskell

自分用メモ。

アウトプットしないと忘れそう。

前書き

下の本を読んだ。

と言っても流し読み程度だったり( ´∀` )。

理由は「そろそろ関数型を勉強してみるか」と思ったから。

普段何を使っているにしても、たまには違うプログラミングパラダイムの勉強をするのは有用だと思う。実際に何かができるようになるわけではないけど、視野を広げること自体は正しい方向性じゃないかな~って思う。

Haskellって何

関数型言語の一種。その中でも「純粋」関数型プログラミング言語のひとつ。

純粋って付くのは代入文が完全にないから(でいいんだよね?)。例えば関数型の中でも有名なLispとかは代入文があるから、「純粋」ではない(でいいんだよね?)

よく知らないが、結構有名っぽい。モナド、モナド|д゚)言っている人もいたりする

どんな感じ

ざっくり以下のような感じ

  • 繰り返しは再帰型 and 蓄積変数で計算
  • 遅延評価
  • リスト内包表記
  • 入出力はモナド

蓄積変数を使って計算

関数型お得意の再帰ですな。業務だと使わないけど、再起を使うときが一番プログラマーっぽいなって思わなくもない。つまりは、

int f(int n) {
    int sum = 0;
    for (int i = 0; i < n; ++i) {
         sum += i;
    }
    return sum;
}

は(なんか雰囲気だけのコード)、こうなる。

int f(int accu, int n) {
    if (n == 0) {
        return accu;
    } else {
        return (accu + n, n-1);
    }
}

うむ。関数型っぽい。

遅延評価

副作用がないため、どの関数を先に評価するかで基本的に違いがない。要するに、

mul(1+2, 2+3)
= mul(3, 5)
= 3*5
= 15

mul(1+2, 2+3)
= (1+2)*(2+3)
= 3*5
= 15

に違いがない。…っていうと当たり前に思えるが、+演算子の部分を何らかの関数呼び出しと考えると…

mul(f(1, 2), g(2, 3))

まあ、そんなに簡単な問題ではなくなる。例えば、f(1, 2)が入力を受け取って、1行目と2行目の数字の足し算を返す関数で、g(2, 3)が入力を受け取って、2行目と3行目の数字の足し算を返す関数とかにすると、fとgの評価順番で結果が異なる。

そんなこんなで副作用がある限り評価順番が重要になってしまうわけだが、副作用がないHaskellならどちらを先に評価しても基本的に一緒なので、必要になるまでは評価されない(遅延評価)らしい。

これで何が嬉しいかというと、無限のデータ構造が扱えるため、なんだか色々と楽なそうな。ここら辺は使ってみないとよくわかりそうもない。

リスト内包表記

まあ言葉は難しいけど、あいつだね。よく見るよね。

list = [i for i in range(10)]

的な。

入出力はモナド

そしてモナド。一応頑張って解説してみる。

モナドはクラスの一種で、return関数と連結演算子が定義されているものを指す。

return関数は以下のようにして定義される

  • return関数はある値vを受け取る関数である
  • 返り値としては関数を返す
  • 返される関数は、文字列sを受け取り[(v, s)]を返す関数である。

例えば、10と”abc”を引数にとる場合は、

(return(10))("abc") = [(10, "abc")]

となる。ここまでは意外と簡単かも。要するにreturnは何もしないで変数を返す関数ということだね。

連結演算子(>>=)は大体以下のようにして定義される

  • 連結演算子は、ある関数fとある関数gを受け取り、関数を返す。
  • fは文字列を受け取り、ある値aと文字列のペアを返す関数である
  • gはある値aを受け取り、g’を返す関数である
  • g’は文字列を受け取り、ある値bと文字列のペアを返す関数である

返される関数に文字列inputを渡す。つまりf >>= gにある文字列inputが与えられると、以下のようにして処理される

  • まずf(input)が適用される。すると、fはその定義から[(x, output)]を返す。
  • 次にg(x)を評価する。つまりg’が計算される
  • 最後にg'(output)を評価する。g’はその定義から[(x’, output’)]を返す

おぉ、意外と書いてみると単純だった。g(x)を評価して再度関数を作るところがちょっとややこしいんだな。でも基本的にはfを適用した後の文字列に再度gを適用しているだけなんだな。

さて、今は入力は文字列だったけど、今度は入力そのものだと思ってみる。すると入出力が作れる。例えば一文字入力を受け取るgetCharはgetChar(W)=[(c, W’)]みたいに定義できる。まあ、副作用がない代わりに返り値としてその状態を返しているわけだな。

同様にいい感じに定義すると、連結演算子も定義できる。

これをさらに今までは文字列と入力に絞っていたが、型変数を使って一般化したものがモナドというらしい。

って感じなのがモナド。最後で走っちゃったからよくわからなくなってしまったかもしれない。このモナドが何が嬉しいのかは、ぶっちゃけよくわからない。

まとめ

この文章を書いてみて分かったが、要するに私はまだ何もわかっていない。本当は何が優れていて、どんな意味を持つ言語なのかを調べないといけないかな。そこら辺までこの本を読めば分かると思ったんだが甘かった。

う~ん。これ以上勉強するのは厳しいな。絶対に仕事で使うことはないし、趣味で書くこともないだろうからな~。

コメント

タイトルとURLをコピーしました