関数型プログラミングはまず考え方から理解しよう
ともぐい、ともぐいをする。 (つまり、からあげのつまみ方とは。)
唐揚げつまんでみた
唐揚げもうひとつだけ
をみていて、なんでオブジェクト指向捨てちゃうの?と_何かが間違っている_思考に捕らわれました。
ということで、凍った唐揚げ弁当なるオブジェクトを考えることにしました。ECMAScript5以前のJavaScriptだとクラスを書くのが面倒現代風1っぽくないので、ECMAScript2015で書きます。
"use strict";
class Bento {
constructor(karaageNumber) {
this._karaageNumber = karaageNumber
}
get karaageNumber() { return this._karaageNumber; }
karaagePick(number = 1) {
return Object.freeze(new Bento(this.karaageNumber - 1));
}
toString() {
return `唐揚げ: ${this.karaageNumber}`
}
}
class BentoSet {
constructor(list) {
this._list = Object.freeze(list.slice(0));
}
get list() { return this._list; }
indexOfMaxKaraageBento() {
return this.list.reduce((accs, bento, idx) =>
accs[1] < bento.karaageNumber ? [idx, bento.karaageNumber] : accs
, [0, 0])[0];
}
karaagePick(number = 1) {
if (number <= 0) {
return this;
} else {
const max_index = this.indexOfMaxKaraageBento();
return Object.freeze(new BentoSet(this.list.map(
(bento, idx) => idx === max_index ? bento.karaagePick() : bento
))).karaagePick(number - 1);
}
}
toString() {
return this.list.join(", ");
}
}
const bentoSet = Object.freeze(new BentoSet(
[
Object.freeze(new Bento(10)),
Object.freeze(new Bento(8)),
Object.freeze(new Bento(6))
]
));
console.log(bentoSet.toString());
console.log(bentoSet.karaagePick(1).toString());
console.log(bentoSet.karaagePick(2).toString());
console.log(bentoSet.karaagePick(3).toString());
console.log(bentoSet.karaagePick(4).toString());
console.log(bentoSet.karaagePick(5).toString());
console.log(bentoSet.karaagePick(6).toString());
console.log(bentoSet.karaagePick(7).toString());
λ node karaage.es6
唐揚げ: 10, 唐揚げ: 8, 唐揚げ: 6
唐揚げ: 9, 唐揚げ: 8, 唐揚げ: 6
唐揚げ: 8, 唐揚げ: 8, 唐揚げ: 6
唐揚げ: 7, 唐揚げ: 8, 唐揚げ: 6
唐揚げ: 7, 唐揚げ: 7, 唐揚げ: 6
唐揚げ: 6, 唐揚げ: 7, 唐揚げ: 6
唐揚げ: 6, 唐揚げ: 6, 唐揚げ: 6
唐揚げ: 5, 唐揚げ: 6, 唐揚げ: 6
お弁当とお弁当セットの二つのクラスを用意しました。唐揚げクラスも用意しようかと思いましたが、taste(味)ぐらいしかプロパティがなさそうなので断念しました。
さて、この弁当(とそのセット)ですが、Object.freeze()
で凍らせています。凍った弁当から一つの弁当だけ取り出して、そこから唐揚げを摘まもうとしても不可能です。だって、凍り付いているのですから、箸で唐揚げ摘まもうとしても、弁当から離れません。
では、どうするかというと、ちょっとだけ違うクローンを作ることにしました。つまり、唐揚げ取り出しちゃった後の弁当を新たな弁当(とそのセット)として作ることにしたのです。それが、karaagePick()
メソッドがやっていることです。
各クラスのメソッドは、副作用も無く、参照透明です2。レシーバも引数の一つとみなせば、純粋関数と解釈することができます。つまり、オブジェクト指向を捨てる必要なんて全くないんです。immutableのオブジェクトにすれば、関数型プログラミングと同じような発想とメリットで作ることができます。
あ、再帰部分って唐揚げが多いとスタックオーバーフローするような。食べ過ぎは良くないですし、あとで考えよう。