CC BY-SA 4.0
- なかやん・ゆーき / ぺんぎん / もみあげ
- @pocketberserker / id:pocketberserker
- Microsoft MVP for
F#(2013/04/01~ 2017/07)- Visual Studio and Development Technologies
明確な線引きはない、はず。
アスキードワンゴから出版された『Haskellによる関数プログラミングの思考法』によると:
- 関数プログラミングはプログラムを構成する方法である。この方法では関数と関数適用を主に使い、命令や命令の実行は使わない。
- 関数プログラミングでは数学で使う単純な記法を用いる。この記法では問題を簡潔明快に記述できる。
- 関数プログラミングは単純な数学を基礎とする。この基礎がプログラムの性質に関する等式論証を支えている。
数学云々の話は議論あるかもしれないので今回はスルー。
let plusOne x = x + 1
plusOne 1 // 2
関数を引数に適用する
から関数適用
。
F#では関数は数値や文字列と同様に値として扱える。
// List.map: 'T list -> ('T -> 'U) -> 'U list
List.map plusOne [0 .. 9] // [1;2;3;4;5;6;7;8;9;10]
関数を渡せる。
// foo: bool -> ('T -> 'T) -> ('T -> 'T)
let foo cond f =
if cond then f
else id
関数を返せる。
人間は上から順にコードを読みたい生き物である。
List.take 3 (List.map plusOne [0 .. 9])
[0 .. 9]
|> List.map plusOne
|> List.take 3
(|>)
は'T -> ('T -> 'U) -> 'U
で、順序を入れ替える。
既存の関数を合成することができる。
let mulTwo x = x * 2
[0 .. 9]
|> List.map (plusOne >> mulTwo)
(>>)
は('T -> 'U) -> ('U -> 'V) -> ('T -> 'V)
。
再帰関数を定義するにはrec
キーワードを用いる。
let rec listLength l =
match l with
| [] -> 0
| x::xs -> 1 + listLength xs
[0..9]
|> listLength
多くのデータ構造ではfold
と呼ばれる関数が定義されている。
// List.fold: ('T -> 'U -> 'T) -> 'T -> 'U list -> 'T
[0..9]
|> List.fold (fun r _ -> r + 1) 0
再帰は何でも書けるが、fold
は簡潔に賭けて便利なことが多い。
fold
で書けない時(あるいはパフォーマンスが関係しているとき)に再帰関数を記述する、という方針を持っておくとよいかも。
let
を遣えば識別子と値を関連付けられる。
let a = 1
束縛した値を置き換えたいがmutable
を使いたくない場合、どうすべきか?
シャドウィングという機能を使えばよい。
let a = 1
let a = 100000
a + 1 // 1000001
最初の束縛は隠蔽されるため、あたかも代入したかのように扱える。
let a = 1 in
let a = 100000 in
a + 1
スコープがあるよ、と考えればわかりやすいかもしれない。