今回は、簡単なプログラミングをしてみます。扱うのは、 Haskell という名前の言語です。 Jumper のデータを保存するサーバにも使われている現在の欠かせない言語です。
例題として、
ベクトル (1, 2, 3, 4) と (4, 3, 2, 1) の内積を求めよ。を扱ってみます。内積の計算といえば、言語処理などでもよく使われる重要な演算処理となります。内積の値が大きければ大きいほど、2つのベクトルに関連性があるということです。
プログラミング言語を扱ったことがある人は、得意な言語でどのようなコードになるのかを想像してみてください。
ちなみに、上の内積の計算は簡単で、
(1 * 4) + (2 * 3) + (3 * 2) + (4 * 1) = 20となります。掛け算して足し合わせるだけです。他の言語だと、 恐らく、5行前後で表現できるのではないでしょうか。簡単な計算のはずが意外と書くことが多くて大変です。
では、Haskell ではどうなるのか。でも、その前に Haskell に関してさらっと勉強していきましょう。
Haskellとは
まず、Haskell は関数型言語です。関数型言語は、手続き型言語と対となって説明される概念です。関数型言語は、数学で勉強する関数を組み合わせることで要件を満たします。それに対して、手続き型言語は、手続き(一連の処理の流れ、ルーチン(routine)、プロシージャ(procedure))を組み合わせることで、求める動作をします。Lisp、Scheme、Ocaml、Haskellなどが、関数型言語。C言語、Perl、Basicなどは、手続き型言語です。手続き型言語は、手続きのことを「関数」と読ぶことが多いですが、ほとんどの「関数」は、関数ではありません。今回は、関数か否かの話は割愛します。
次に、Haskell は静的型付け言語です。僕が、Haskell を好む理由は、こちらにあります。静的型付けによって、複雑なプログラムもミスが少ないことを保証することができるのです。こちらも、詳しい話は割愛します。
インストール
https://www.haskell.org/platform/ からインストーラをダウンロードしてください。ダウンロードが完了したら、ダブルクリックしてインストールしてください。あまり大変な作業は無いかと思います。
ghci
ghci を使うことで簡単に Haskell のプログラミングを触ることができます。コマンドプロンプトまたは、ターミナルを開いて、以下のコマンドを打って開始です。
ghci
これだけです。ghci> という表示が出てきたと思います。
では、簡単な計算をしてみようと思います。
1 + 1
2 と出てきたら成功です。
では、どんどんやっていきましょう!
リスト操作
Haskell において重要なデータ構造が、リストです。リストを制するものが Haskell を制するといっても過言では無いでしょう。let a = [1, 2, 3, 4]これで、リストの定義は完了します。let は、「…を〜させる」という意味の単語ですね。この場合、a を [1, 2, 3, 4] と定義するという意味になります。
[1, 2, 3, 4] というのがリストです。他の言語とほとんど同じですね。
head aとすると、先頭の 1 が取り出せ、
tail aとすると、[2, 3, 4] が取り出せます。
last aの結果は、4 です。
なんか、退屈ですね。。。こういう関数は、使いながら慣れていけばよいので、こういった関数は、今覚える必要はありません。
リストという複数の要素を扱うための型があるということを認識できればそれでよいです。
foldr
foldr (フォールドアールと読みます)は、畳み込みという名前の付いた関数です。先ほどの a を操作してみましょう。foldl(フォールドエル)もありますが、ほとんど同じものだと考えて構いません。foldr (+) 0 aとしてみてください。結果は 10 になります。この場合は、foldl と foldr と書き換えても結果は同じになります。総和(sum、サム)を計算するロジックだと思ってください。
ここで、他の言語で総和を計算することを考えてみてください。恐らく、数行のコードになるかと思います。それが、学校で習う数式のようにシンプルに1行で表現できてしまいました。
これが、Haskell のリスト操作に対する強力さであります。
上の結果がなぜ 10 になるのかを説明するのは、今回の趣旨からそれますので、fold (+) 0 はリストの総和を求める関数ということを覚えておいてください。
zip, zipWith
zip(ジップと読みます)というと、一番最初に思いつくのは、ジップロックでしょう。または、人によって、チャック(zipper、ジッパー)のことと思うかもしれません。どちらも語源は一緒です。zip というのは、2つのものを一緒にすることを短い単語で表現しています。
zip [1, 2, 3] [-1, -2, -3]とすると、
[(1,-1),(2,-2),(3,-3)]が返ってきます。
(1, -1)などは、タプルと言って、前にやったようにペアのことだと捉えていただければよいです。しかし、これでは、あまり、メリットが感じられません。では、zipWith という強力な関数を使ってみましょう。
zipWith (+) [1, 2, 3] [-1, -2, -3]zipされた、ペアに対して足し算(+) が適応されると解釈してください。結果は、
[0, 0, 0]になります。どうですか、ピンときましたか?
いよいよ
やっと、ベクトルの内積を計算する準備ができました。foldr と zipWith を組み合わせることで実現しそうですね。まずは、ベクトルを Haskell のリストとして表現します。
let b = [1,2,3,4]そして、
let c = [4,3,2,1]
let dot x y = foldr (+) 0 $ zipWith (*) x yとします。掛けてから足し合わせる。これで、内積が求められますね。
dot b cすると
20できましたね!
手続き型の言語では、ループを最初にイメージして、配列から1つずつ要素を取り出して操作する習慣がありますが、Haskell では、リストを使って要素をまとめて扱うということをします。
こういった習慣や考え方のことをパラダイムと言ったりします。プログラミング言語をマスターするということは、まさにこのパラダイムを頭に馴染ませるということに他なりません。
参考書があるとすると、その参考書を二周三周するか、同様の書籍を数冊読みこなし実践するうちにようやくパラダイムが頭の中にできあがり、プログラミング言語をマスターするということになります。
今回はここまで。皆さんも諦めずに、どんどん自分なりに課題を見つけて取り組んでみてください。