この記事は Crowdworks Advent Calendar 2015 16日目の記事になります.
こんにちは、どうでしょう藩士の飯田です。
DVD第24弾「ユーコン川160キロ~地獄の6日間~」が本日から予約が始まりました!
ユーコンは名脇役(女性現地ガイド熊谷さん)あり、名言ありなので今から楽しみです!!
ちなみに個人的には「ひとり不思議発見」が好きですw
はじめに
前置きはこれくらいにして、本題に入ります。
Scala
で関数型言語を初めて触り、関数型言語に興味を持ちました。
関数型言語とは?
ちなみに関数型言語とはどんな言語のことを言うのでしょうか?
関数型言語とは?(Wikipediaからの引用)
関数型言語(かんすうがたげんご、英: functional language)は、以下に述べる関数型プログラミングを基本スタイルとして推奨する機能を持つプログラミング言語、関数型プログラミング言語[1]の略称である。
つまり、関数型プログラミングとは?(Wikipediaからの引用)
何をもって関数型プログラミングとするか、関数型プログラミングを行っているコミュニティ内でも正確な定義や合意というものは存在しないが、一般的には、手続き型プログラミングがコマンド実行の列としてプログラムを記述していくのに対し、関数型プログラミングは複数の式を関数の適用によって組み合わせていくプログラミングスタイルである、ということは広く認められている。
と、いうことなんですね。
確かにScala
で実装するとき関数を組み合わせてコーディングしてました。
ここが手続き型プログラミングとの違いで、とっつきにくい部分かもしれないです。
関数型に慣れてくるとこの組み合わせる感覚がビシっとハマった時気持ちいいんですよねー
メリットは?
あと、関数型言語のメリットです。(お約束的な)
- 第一級関数・高階関数
- 副作用がない
副作用がない
ことによって、テストがしやすかったり、部分プログラム化しやすかったりなどの恩恵を受けることが出来ます。
代表的な関数型言語
そういえば、Scala
以外にどんな関数型言語があるのでしょうか?
調べたら結構いろんな言語がありました、結構ね。
言語 | 純粋さ | 型付け |
---|---|---|
Clean | 純粋 | 強い、静的 |
Erlang | 非純粋 | 動的 |
F# | 非純粋 | 強い、静的 |
Haskell | 純粋 | 強い、静的 |
Idris | 純粋 | 強い、静的 |
Lazy K | 純粋 | 型なし |
LISP | 非純粋 | 動的 |
Miranda | 純粋 | 強い、静的 |
ML | 非純粋 | 強い、静的 |
SML# | 非純粋 | 強い、静的 |
Standard ML | 非純粋 | 強い、静的 |
OCaml | 非純粋 | 強い、静的 |
Scala | 非純粋 | 強い、静的 |
Scheme | 非純粋 | 動的 |
Unlambda | 非純粋 | 型なし |
※Wikipedia引用 |
非純粋
と純粋
がある
また、関数型って非純粋
、純粋
の2パターンあるんですね!
純粋
な関数型どんなのか気になる!
という単純な興味本位から今回、純粋関数型言語Haskell
を触ってみました!
ちなみに、純粋関数型言語とは?(Wikipediaより引用)
純粋関数型言語では、参照透過性が常に保たれるという意味において、全ての式や関数の評価時に副作用を生まない。純粋関数型言語であるHaskellやCleanは非正格な評価を基本としており、引数はデフォルトで遅延評価される。一方、Idrisは純粋だが正格評価を採用している。入出力などを参照透過性を保ったまま実現するために、たとえば Haskell ではモナド、Clean では一意型(英語版)という特殊な型を通して一貫性のある表現を提供する。
つまり、副作用が全くない
ない関数型言語ということです。
なるほど、確かにScala
は再代入できる(var
など)ので非純粋
とうことで納得です
- 変数定義の時に値を設定し、その後値を再代入できなければ副作用は起きない
なぜ今回、純粋関数言語の中でHaskell
を選んだかは、関数型言語で調べた時にLISP
やHaskell
がよく検索でひっかかり、その中で静的型付け
がHaskell
だったからです。
Haskellのインストール
まずはHaskell
が実行できるようにするため、Macにインストールしていきます。
※ Windowsはちょっとわかりません・・・
brew
でサクッとインストールできるんですが、バージョンが古くなるということで
今回はパッケージからインストールします。
ghc をイントールする
- http://ghcformacosx.github.io/ からzipファイルをダウンロードして、インストールする
- zipファイルを解凍し、実行ファイルを Applicationフォルダに移動します
- ファイルを実行し
Append to ~/.bash_profile
をクリックし、Modify
を選択する
以上でインストールは終わりです、簡単ですね!
ghcが無事インストールされたか確認します
$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> print "Hello World"
"Hello World"
Prelude>:quit
Leaving GHCi.
はい、お決まりのHello World
が出力されました。
めでたしめでたし。
Haskell
を使ってみた
フィボナッチ数列
関数型言語は再帰が得意ということなので、フィボナッチ数列をやってみました(正確には調べました)。
fibonacci = 1:1:zipWith (+) fibonacci (tail fibonacci)
ホント1行でかけるんですね、でもかなりキモいです。
※このページを参考にするとわかりやすいです。
そして、実行するとこんな感じ。
$ ghci
HCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :l fibonacci.hs
[1 of 1] Compiling Main ( fibonacci.hs, interpreted )
Ok, modules loaded: Main.
*Main> fibonacci
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155,165580141,267914296,・・・
と、メモリを食い尽くすまで無限に実行されますw
ちなみにこれをJava
で書くとこんな感じです。
Haskell
と違いfor文を使い、再代入(i++
やa[i] = a[i-1] + a[i-2]
など)が発生してます。
import java.io.*;
public class Fibonacci
{
public static void main(String[] args) throws Exception{
int num = Integer.parseInt(args[0]);
int[] a;
a = new int [num];
for(int i = 1; i < num; i++){
if(i == 1){
a[i] = 1;
}
else{
a[i] = a[i-1] + a[i-2];
}
System.out.println(a[i]);
}
}
}
Haskell
に比べるとはるかにコード量が多いですよね。
関数型は再起処理が書きやすいのが見て分かります。
ただフィボナッチ数列
だけでは、非純粋
でも同じような恩恵を受けられるので、
まだ純粋
のこんなところが良いというのは無いですね。
ちなみにScala
でもHaskell
と同じく1行でかけます。
Haskell
よりScala
のが読みやすい(慣れの問題ですかね?)のでScala
で十分かなって感じです。
val fibonacchi: Stream[Int] = 1 #:: fibonacchi.scanLeft(1)(_ + _)
最後に
純粋
は非純粋
に比べて◯◯的なことを書こうと思ってたのですが、
関数型言語
の話になってしまいました。。。すみません。
次回はHaskell
で何かアプリを作って、こんな時副作用
がなくてよかった、副作用
が欲しかったなどを書こうかと思います。
エンジニア絶賛募集中!
弊社では自らの技術で「21世紀の新しいワークスタイル」を実現したいエンジニア募集しています!
https://www.wantedly.com/projects/34041