関数型プログラミング
ちゃんと勉強したい
JavaScriptにも関数型の特徴がある
関数型プログラミングは宣言型プログラミングという、より大きなパラダイムの一部
計算のモデルの違い
命令型
計算とは命令を実行すること
チューリング機械
関数型
計算とは関数を呼び出して値を得ること
アロンゾ・チャーチ
論理型
計算とは証明を得ること
使いたい言語
Scheme
Lisp
Immutability
不変性
副作用がない
変数の値を変えない
0から99までの数値を出力するプログラム
命令型の場合、変数iに値を保持し、状態を変更し、その結果を出力する
code:imperative.js
for(int i = 0; i < 100; i++) {
console.log(i);
}
関数型の場合、再帰を使い、iの状態は変更しない
code:functional.js
function f(i) {
if(i > 99) {
return;
} else {
console.log(i);
return f(i+1);
}
}
f(0);
Referential Transparency
参照透過性
関数を値と置き換えても結果が変わらない
Date.now()は評価の度に異なる値が返ってくるので参照透過でない
purityとも近い概念
同じ入力に対して同じ出力が返ってくる
副作用がない
ファイル書き出しやエラー処理は副作用
Higher Order Functions
高階関数
関数を引数に取る関数
リストの中身を2乗したい場合
命令型
code:imperative_square.js
function square(nums) {
let squared = [];
for(let i = 0; i < nums.length; i++) {
squared.push(numsi * numsi); }
return squared;
}
関数型
code:functional_square.js
function square(nums) {
return nums.map(function(n) { return n * n; });
}
Lazy Evaluation
遅延評価
必要になるまで評価されない
Haskellは遅延評価がデフォルト
対義語はEager Evaluation(先行評価)
関数が呼ばれる度にif文が評価される
code:eager_evaluation.js
function myIf(condition, thenAction, elseAction) {
if(condition) {
thenAction();
} else {
elseAction();
}
}
Lambda calculus
ラムダ計算
関数型プログラミングの基礎
JavaScriptのアロー関数はラムダ計算の直接的な翻訳
JavaScriptでの使い所
1回だけ実行する
ボタンを1回だけ押せるようにしたい場合
グローバル変数でフラグを使う
実行されたらイベントハンドラをnullにする
などが考えられるが、使い回せなかったり状態の管理が複雑になったりする
code:once.js
const once = (fn) => {
let done = false;
return (...args) => {
if (!done) {
done = true;
return fn(...args);
}
}
}
const f = message => console.log(message);
const doOnce = once(f);
doOnce("hi!");
doOnce("hi!");
数学の知識
Set Theory(集合論)
domain(定義域)とrange(値域)
集合と写像
Category Theory(圏論)
必須ではないが知っておくと理解の助けになる
Objects(対象)とMorphisms(射)
AとBが圏Cの対象であるとき、AからBへの射は$ A \rightarrow Bと書く
厳密には違うが、対象は集合で射は関数とみなせる
参考