技術記事 by 岡部 健

IQ145で美少女JKな先輩に「関数型プログラミング」を特訓してもらえた僕の5日間の記録 (backup)

ユーザ登録

IQ145で美少女JKな先輩に「関数型プログラミング」を特訓してもらえた僕の5日間の記録

  • 26
    Stock
  • 0
    Comment
Stock this post
  • ozw_sei
  • tkwn1
  • kiimiiis
  • FScoward
  • botesan
  • sachaos
  • yoya
  • adate
  • YusukeHosonuma
  • chocolamint
  • ...
TOC
とある出版社からお声をかけていただき、来年、関数型プログラミングの本(紙メディア)を出す、かもしれません。
プロトタイプは筆者の中ですでに出来ているのですが、今後、編集者の方と企画を詰めながらガンガン、ブラッシュアップしていきます。なので製品は、現状のタイトル、内容から大幅に変更される予定です。
未完ながら、お知らせと立ち読みをかねて、ここでプロトタイプの導入部分+第一章をまるごと先行無料公開します。
これだけでも関数型プログラミングとはいったいどういうものなのか?
十二分にアイデアは掴めるように設計しています。
もちろん企画がこける可能性もあるわけで、そのときは別の出版社から出すか、もしくはここで全文公開します。
来年いつになるか未定ですが無事に出版されたら是非、買って読んでみてください。
あと、
量子コンピュータが超高速である原理と量子論とそれに至るまでの科学哲学史をゼロからわかりやすく解説
を結構、時間をかけて書いています。めちゃくちゃ長いです。
評判も上々のようなので、こちらも読んでみてください。

登場人物

セキヤ
高1男子。都内の進学校に通っている草食系チェリーボーイ。
プログラミングが趣味でコンピュータ部に入部した。
サクラ先輩
高2女子。IQ145の知能を持つ美少女。セキヤを厳しく指導する。
コンピュータ部では、いち早く頭角を現し、現在は高3部員を差し置いて部長として君臨する絶対的存在。
プログラミングスキルは『神の眼』と呼ばれる全能レベルにまで到達していると一部では噂されているが、サクラのコードを読み解けるだけのスキルをもつ人材が部内にいない為、今のところ真相は不明である。
[TOC]

~プロローグ~

「ずいぶんとダサいコードを書いてるのね。」
不意に背後から声をかけられ振り向いて見ると、透き通るように白い首筋が目の前にあった。
「はっ、サクラ先輩!」
スラリとした長身を前かがみにしてモニターを覗きこむように見ている長い黒髪の少女の存在をセキヤが理解するまでに一瞬の間が必要だった。
放課後の電子計算機室。コンピュータ部の活動に自由に使用して良いことになっている。今日は他の部員は誰もおらず、セキヤ一人きりで黙々と作業をしていたのだが、いつのまにかコンピュータ部の部長であるサクラが様子を見に来ていたようだ。
「セキヤ君は何のプロジェクトやってるんだっけ?」
セキヤの反応を意に介することもなく同じ姿勢でモニターに表示されたコードを頭上からの冷たい目で凝視したままのサクラ。
微かに甘い良い香りがしてくる。実際、お互いの息がかかりそうなほどの至近距離にセキヤは明らかに動揺していた。また自分の両方の耳は赤くなってしまっているのだろうな・・・先週サクラになじられた事を思い出しながらセキヤは精一杯の平静を装って答えようとする。
「えっと、今は何もやっていないです。プログラミングコンテストの地区予選に向けて過去問に挑戦しているところなんです。」
「あーそうなんだ。だけどこんなダサいコード書いてたら絶対勝てないよ。残念だけど。」
今日のサクラはセキヤの動揺をなじることよりも、セキヤが書いたコードのほうに関心があるようだ。おかげで少し余裕を取り戻しながら、セキヤは言ってみる。
「えー?そんなにひどいですか。」
「ダサいわね。まったくお話にならないわ。」
見下すような冷淡さでバッサリと切り捨てられる。
「そうですか・・・どこがまずいのでしょうか?」
セキヤはこんな風にサクラと何気なく会話できている自分が嬉しい。内心は小躍りしたいような気分なのだが、表向きは平然と会話を続けられることが嬉しい。
「教えて欲しいの?」
居直ってはじめてこちらを見たサクラの知的な目がキラリと光る。
のぼせ上がっており、もはや目の前のコードやプログラミングコンテストの地区予選のことなどはどうでもよくなっていたセキヤではあるが、ただただサクラとのこの時間を持続させたい一心で上辺の調子を合わせる。
「教えていただけると嬉しいです。」
「地区予選はいつだったっけ?」
「来月の頭くらいですね。」
少しのあいだ思案した後、サクラはおもむろに言う。
「まあ、ギリギリ間に合うかも。ちょっと特訓してみる?
自分の熱心さが部長のサクラに認められたのだろうか?厳しいが美しく、部員への面倒見もいいサクラは絶大な人気がある。
雲の上のような存在であるサクラからの思ってもみないオファーにセキヤは心がうち震えるようだった。
 
この高校に入学できてよかった。コンピュータ部に入部して正解だった。今、この瞬間、僕は青春を謳歌している!セキヤは自分の幸運と幸福を噛み締めていた。

Day1 それは「関数型プログラミング」という新世界の幕開けだった

0から9までの数をすべて足すコードを書け

サクラは無造作に近くにあった椅子を引っ張ってきて、セキヤの隣に姿勢よく座った。
「セキヤ君は、プログラミング言語は何を使いたいの?」
再びサクラと寄り添うような格好になってしまった上に至近距離で自分の名前を呼ばれて著しく混乱してしまっているセキヤだが、なんとか振り絞るように答えた。
「JavaScriptです。」
「良い選択ね。ではまず、基本的なところから始めてみましょうか。
0から9までの数をすべて足すコードを書いてみて?」
「はい。余裕です!」
問題に向かえば、セキヤは不思議と心は落ち着く性分だ。
躊躇なく書き慣れたコードをタイプしていった。
var s = 0;
for (var n = 0; n < 10; n++)
{
  s = s + n;
}

console.log(s);
45
「ほら、どうです!こたえは45。」
軽い達成感とともにセキヤは、サクラへ誇らしげに言う。
「動くだけで、やっぱり超ダサいわ。」
「え?」
「今どきこんなダサいコードを書いているようでは地区予選で他のプレイヤーに瞬殺されちゃうわね。」
「そうなんですか!?でもどこがダサいのかさっぱりわかりません。先輩がコード書いて、ダサくない見本を見せてくださいよー。」
あえて甘えた感じでセキヤは言ってみた。
「仕方がないわね。私ならクールにこう書くわ。」
キーボード上に細長く美しい指が流れる光景に、しばしセキヤは見惚れてしまっていた。
「どうかしら。」
サクラの声に我に返ったセキヤ。コードをじっくりと見てみる。
var plus = function(a, b)
{
  return a + b;
};

var s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  .reduce(plus);

console.log(s);
45
「へ?なんだこれ?」
「クールでダサくないイケてるコードよ。」
「よくわからないですね。とりあえず僕のコードのほうが短くないっすか?」
「今のところはね。」
「あと、ダサいと言われ続ける今の僕のスキルじゃ、先輩のコードは意味がわからないし、ごちゃごちゃしてるようにも見えます。」
「生意気ね。ごちゃごちゃうるさいのはあなたよ。今からそれを説明してあげるんだから、ちょっと黙れ小僧。」
イラッとしたサクラに叱咤されてしまった。
「申し訳ありません、サクラ先輩。大変失礼いたしました!」
セキヤは半ば恍惚としてしまっていた。

フローは複雑でバグの元凶

サクラはちょっと考えてから作図アプリを立ち上げ、手際よくグラフを作成していった。
enter image description here
「セキヤ君、これが何かわかる?」
「もちろんです。フローチャートと呼ばれるもので、このフローチャートは僕が書いたコードの流れ、つまり僕のコードのフローをグラフ化したものです。」
サクラは満足そうに頷いた。
「正解。そして今回の問題はなんだったっけ?」
『0から9までの数をすべて足すコードを書け』でした。」
「そうね、では、このフローチャートをぱっと見て、
これが『0から9までの数をすべて足すコードを書け』の解法を示していると即座に答えられる人はいったいどの程度いると思う?」
「たしかに、わかりにくいでしょうね。でも少しは居ると思いますよ。」
セキヤの若干の反抗を感じ取り、またちょっとイラついた様子のサクラではあったが会話を続ける。
「いいわ。では仮にこれが『0から9までの数をすべて足すコードを書け』という問題ぽいな!とすぐ見抜けるほど気の利いた人がいるとしましょう。
でもこのフローチャートで表されるコードが本当に『0から9までの数をすべて足すコードを書け』の解法として合っているのかどうか?確信をもって断言できる人はどのくらい居るのかしら?」
「それはまず居ないと言って良いんじゃないでしょうか。コードにすぐバグが紛れ込む事はプログラマならば誰でも身を削るようにしてよく理解しているはずです。コードが正確かどうか確信をもって断言できると思ってるプログラマが居るとしたら、そいつはモグリでしょう。」
サクラは再び満足気に頷いた。
「よくわかってるじゃない。つまりセキヤ君のコードはかんたんなようで実はかなり複雑なのよ。コードの妥当性を検証するためには、頭の中でフローチャートに従って順番に変数の値を追っかけて何度も確かめてみたり、手っ取り早いのは実際にコンピュータでコードを走らせてみてエラーが出るか試してみる事でしょうね。そのほうが速くて正確。でも、エラーが出ないバグが一番やっかいね。だから最終的には変数をウォッチするデバッグ機能なども利用して変数の値を逐次検証していく必要はあるわ。」
「おっしゃるとおり、コードのフローを逐一追跡しながら隠れたバグを探し出すのはものすごい骨の折れる作業ですよね。よくわかります。」
「そして、これこそが多くのコードが抱える根本的な問題なの。バグを解決できずにプロジェクトそのものが頓挫することはままあるわね。セキヤ君のコードがダサいと言われる理由もそこなの。フローは複雑でバグの元凶なのよ。

フローは不要

「先輩の言うことはもちろんよくわかるんです。でもプログラミングって元々そういうものなんじゃないんですか?フローがあってこそのコードですよね?」
「だから、その発想がダサいのよ。」
うんざりとした調子でサクラが言う。
「そうなのかな?」
『0から9までの数をすべて足す』という問題には、繰り返しや条件判断といったフローはあるかしら?」
『0から9までの数をすべて足す』という問題自体には、繰り返しや条件判断といったフローはまったく確認できないですね。」
「では何故、セキヤ君のコードにはそんな複雑なフローの存在が確認できるのかしら?」
「それしか方法がないからですよ。この問題を解くには、繰り返しや条件判断といったフローを含むコードを書くしかない。フローは絶対に避けられないと思います。」
「ほんとうにそう?私のクールなコードはどうかしら?」
var plus = function(a, b)
{
  return a + b;
};

var s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  .reduce(plus);

console.log(s);
「うーん、言われてみればフローが消えてるなあ。僕のコードにあったforループの繰り返しや条件判断がすべて綺麗さっぱり消えてなくなっています。
「少しは成長したわね。」
サクラはまた満足気だ。
「いい?フローをもってコードを書くことは唯一の方法ではないの。むしろフローを書くことは複雑な仕事でバグの温床となるダサいやり方なので、フローは極力さけるべきなの。」
「なるほど。」
『0から9までの数をすべて足す』という問題にフローはない。ならば、コードもそのままフローなしに書ければいいんじゃないかしら?」
「それが出来るならば言うこと無いです。コードはクールになりますね。」
「クールなコードでは、フローの設計やフローの妥当性の検証に労力を費やすようなダサい真似はしないのよ。」
「つまり、プログラミングの最大の課題であるデバッグの手間が激減するわけですか?それはかなりクールなことになってしまう。」
「わかってきたじゃない。」
フローじゃなくて不要(フヨー)ですね。
「ダサいダジャレだけど、まさにそのとおりよ。」
口元をゆるませながらサクラは続ける。
「でもフローが不要という引き算だけでは、私のコードの真価を理解しているとはとても言えないわね。」
「というと?どういうことでしょうか?」
 

フローを書かず論理をそのままコードに書き写せ

「フローがない、フローの設計が必要ないということは、問題の論理だけに集中している、ってことに他ならないの。ここ重要。セキヤ君は、プログラミングで後々どうせ膨大な検証が必要となるフローの設計に時間を費やしたいかしら?それとも問題の論理だけに時間を費やしたいかしら?」
「それはもちろん、問題の論理そのものだけに集中してコードを書いていきたいです。」
「そうでしょう。私のコードのクールさの真価はそこにあるの。問題の論理そのものに集中しているの。」
「もう少し説明をお願いします。」
「いいかしら?『0から9までの数をすべて足す』という問題について、私のクールなコードでは、
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //0から9までの数
  .reduce(plus); 
  //をすべて 足す
と、問題の論理そのものを単純にコードへ、まる写しにしているだけなの。」
「あ!?なんで?凄い・・・先輩、やっと見えてきました!」
「わかった?私のクールなコードでキモとなる部分はたったこれだけ。問題の論理そのものを書き写しただけなんだから、バグが介入する余地なんて最初からどこにもあるはずがないでしょ?」
「まったくないです。」
「ということは、つまり、私は本当に『0から9までの数をすべて足すコードを書け』の解法として合っていると確信をもって断言できるわけ。
プログラミングの世界で、論理のみで構成された、これ以上望めないほど見通しの良いクールなコードを書くことは、ダサいコードを平気で書く常人プログラマーの想像を超えた『コードを見通せる眼』をもつことに等しいのよ。」

『神の眼』のスキル レベル0

All-Seeing Eye of God
米1ドル札の『すべてを見通す神の全能の目』
(All-Seeing Eye of God)
このサクラの発言で、セキヤには少し思いあたる節があった。
「先輩、それはひょっとして『神の眼』のことでしょうか?部員がサクラ先輩は『神の眼』のスキルを持っているとかなんとか噂しているのを聞きました。」
「あー、さあどうかしら?」
「これは『神の眼』のスキルなのですか?」
「なんとも言えないわね。」
「先輩、教えてください。噂の真相を知りたいです。」
「しつこいわね。まだレベルが低すぎるのよ。『神の眼』と呼ぶなら、これはまだレベル0のスキルだから。」
面倒になったのか、あきらめるようにサクラは答えた。
「やはり『神の眼』のスキルは本当に存在するんですね!」
「私自身は、単に『見通しの良いクールなコード』とか『コードを見通せる眼』、と普段言っているわ。それを彼らが大げさに誇張しているだけなんじゃないかしら?
だいたい『神の眼』と騒ぐようなたいしたことでもないの。」
「きっと先輩はIQ145あるからそう思うんですよ。部内では先輩のコーディングのスピードとコードにバグが出ないことは有名だし、僕らにしたら先輩が『神の眼』を持ってるとしか思えないんです。」
「そう呼びたければ『神の眼』でもなんでも好きにすればいいわ。」
まったく興味がなさそうに答えるサクラ。 
「先輩、僕は先輩のように『神の眼』のスキルが欲しいです。今の僕はまるでダメですね。これまで、問題の論理を上手にフローに変換してフローを設計する作業こそがプログラミングだと勘違いしていたようです。ダサい己のダサさを認めざるを得ません。」
セキヤはここぞとばかりに自分の熱心さを懸命にアピールしてみた。
「そうね、その自覚がもてればようやくスタート地点に立てたということ。おめでとうセキヤくん、今あなたは、レベル0よ。」
「ありがとうございます。『神の眼』のレベルがあがるように頑張ります!」

『神の眼』を得るための唯一の方法

「まったくしょうがないわね。
とにかく今後『神の眼』のレベルを上げていきたいのであれば、
問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくということを常に肝に銘じて置いて頂戴。これがクールなコードを書くための唯一の方法よ。」
「了解しました。」
「もういちど私のクールなコードでキモの部分を確認するわよ。
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //0から9までの数
  .reduce(plus); 
  //をすべて 足す
問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくの。
まず「0から9までの数」という論理があるならば、フローでごちゃごちゃやることなど考えないで、単純に[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]そのままのデータまるごと用意してしまうの。」
「わかります。」
「次に、その用意されたデータへ、『をすべて足す』という論理操作をしてやるのね。」
.reduce(plus); //をすべて足す
という部分ですね。」
「これを合わせると、『0から9までの数をすべて足す』という論理をコードに書き写したことになるの。ひたすら問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくわけ。わかる?」

関数 ・function という論理操作

「 先輩、
 .reduce(plus); //をすべて足す
ここなんですが、reduceとplusも2つともJavaScriptでいう関数ですよね?」
「そうね、関数について説明しておいたほうがいいわね。」
「僕はざっくりとしか理解してないので、おねがいします。」
関数というのは中学校の数学の授業でも習うとおり、もともとは数学用語なの。それがそのままプログラミング用語に転用されたものね。
数学・プログラミング用語として、英語ではfunction、それを和訳したものが関数なの。ぶっちゃけ、関数というのはfunctionの翻訳語としてはさほど気の利いたものだとは思えないわね。
そもそものfunctionとは、機能、動作、操作、作用、という意味
だから関数=function=機能、動作、操作、作用とざっくりイメージしておいて頂戴。」
サクラは、また作図アプリで手早く何か描き始めた。
enter image description here
「だいたいこういう感じのイメージよ。
INPUT(インプット)とOUTPUT(アウトプット)というのはわかるわよね?」
「半ば日本語にもなっているので、それはよくわかります。インプット=入力があってアウトプット=出力がある、というやつですね。」
fという名前のFUNCTIONがあるの。これにxをINPUT(入力)してやると、fが持ち合わせる機能によって作用され変化したOUTPUT(出力)が出てくるわ。
このOUTPUT(出力)は、fで作用して変化したという印(しるし)としてf(x)と書き表す習わしになっているわね。
x
f
f(x)
ということ。」
「工場にあるマシンみたいなものですね。」
「まあ、工場に限らないわよ。家庭にある
炊飯器、電子レンジみたいなマシンもFUNCTION・関数なの。
炊飯器という名前のFUNCTIONがあるわね。
これに米と水をINPUT(入力)してやると、
炊飯器が持ち合わせる機能によって作用され変化した
炊きたてのご飯というOUTPUT(出力)が出てくるわ。
x
f
f(x)
という表記でやると、
米と水
炊飯器
炊飯器(米と水)
ちょうどこうなるわ。
このOUTPUT(出力)である炊飯器(米と水)とは炊きたてのご飯なので、
炊きたてのご飯 = 炊飯器(米と水)
という論理操作の関係、論理構造になっているわね。」
「関数・functionの意味を100%理解しました。」
「そうね、でももうちょっとガツンとくるイメージはないかしら?
ああそうだ、セキヤ君は『大改造!!劇的ビフォーアフター』という庶民のための娯楽番組は知ってるかしら?」
育ちの良いサクラによる「庶民」という言葉遣いが少々気になったもののセキヤは答える。
「もちろん知ってますよ!
『様々な問題を抱えた家』があって、超能力者みたいな『匠』がなんかやったら、家が『大変身』しちゃう国民的オカルトドラマです。
Before(ビフォー)とAfter(アフター)の凄まじい変化を見たサザエさんの声の人が『なんということでしょう!!』って感激して、家族も感激して、その家庭には恒久的平和が訪れます。」
「まったくそのとおりよ。
INPUT(インプット)とOUTPUT(アウトプット)というのはこの際、Before(ビフォー)とAfter(アフター)とセキヤ君の脳内で変換してもらっても別に構わないわよ。」
「わかりました。」
Before(ビフォー)の『様々な問題を抱えた家』は
『匠』の手が加わると
After(アフター)の『なんということでしょう!!』
に『大変身』するの。
(Before)『様々な問題を抱えた家』 
↓『匠』
(After)『なんということでしょう!!』
『匠』はFUNCTION・関数よ。
だから、『匠』に 『様々な問題を抱えた家』というBefore、言い換えるとINPUTを(入力)してやると、
匠(様々な問題を抱えた家)となり、
その『大変身』したAfterは『なんということでしょう!!』なので、
なんということでしょう!! = 匠(様々な問題を抱えた家)
という論理操作の関係、論理構造になっているわね。
念の為だけど、『なんてことでしょう!!』というのは、『大変身したリフォーム後の家』のことで、ただ単にそう書くとツマラナイから象徴的に『なんてことでしょう!!』と言い表しているだけだからね?」
「先輩よくわかっています。」
「こんなのイチイチ説明させないでよ?」
『関数・functionというものは、作用して変化させる、という論理操作』であることがよくわかりました。」

関数は論理の最小単位の部品として最上の扱いを受ける

「先輩、
 .reduce(plus); //をすべて足す
で、 reduceplusという2つの関数が組あわされて『//をすべて足す』という関数になってしまっているのでしょうか?」
「まさにそのとおりよ。
『//をすべてxx』と『足す』という2つの関数が組み合わさっているの。」
「2つ関数が組み合わさって、『//をすべて足す』という関数になるんですね。」
問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくためには、あらゆるものがレゴブロックのように組み合わされることが超重要。
関数は、レゴブロックのような最小単位の部品なのよ。論理の最小単位としての部品。
「先輩のコードをよく調べてみると、
 .reduce(plus); //をすべて足す
のうちplusという関数は、
var plus = function(a, b)
{
  return a + b;
};
という形で『足す』という論理の最小単位で部品として独立しているんですね。」
「そう、そしてこのレゴブロックのように自由自在に組み合わせられる部品は、
第一級オブジェクト(first-class object)ってクールに呼ばれているわね。」
「というと、JavaScriptの関数はファーストクラスのオブジェクトですか?クールですね。」
「ええ、JavaScriptの関数はゴリゴリのファースクラスよ。ファーストクラスな部品である関数は、ファーストクラスなので第一級、最上級のクールな扱いを受けるわ。
『足す』という論理操作は、それ自体が最小単位として独立しており、きちんとファーストクラスの最上級の扱いを受ける資格があるのよ。」
「先輩、関数が論理の最小単位の部品、っていうことは、さっきの関数としての炊飯器や『匠』も最小単位の部品として最上の扱いを受けるってことですね?」
「もちろんそうよ。炊飯器や『匠』はファーストクラスで最上の扱いを受けるわ。
ご飯がなくなったら炊飯器さえあれば、また炊けるじゃない。匠がいないと番組は成立しないわ。論理操作としての関数こそが重要なの。」
「よくわかりました。そういえば、CとかC++とかJavaとか、確か関数はこんな論理操作の部品となる特権など与えられていなかった気がします。」
「与えられていないわね。だからJavaScriptとは違ってこういうクールなやり方をするのはかなり難しくなるわ。」
「関数がファーストクラスかどうか?っていうのはプログラミングではかなり重要なスペックなんですね。今後プログラミング言語を調べるときには注意してみます。」
FUNCTION・関数は論理の最小単位の部品で最上の扱いを受ける。ここはクールなコードにとって重要なところよ。」

問題の論理には結果など含まれていない

「先輩、炊飯器や『匠』はファーストクラスで最上の扱いを受ける価値があることはわかりました。
では、『炊きたてのご飯』や『なんということでしょう!』はどうなんですか?」
「それらもファーストクラスの部品であり、もちろん自由に取りまわせるけど、クールなコーディングでは価値はないわね。」
「『炊きたてのご飯』や『なんということでしょう!』は重要ではないってことですか?」
「まったく重要ではないわ。」
「何故ですか?」
「それらは『OUTPUT』であり『After』であり『結果』にすぎないからよ。」
「でも、OUTPUTとか結果は重要なんじゃないかな・・・」
イラッとしたサクラはセキヤをキッと睨みつけながら言う。
「セキヤ君、あなたまだ何にもわかっていないのね!」
「す、すみません。」
「もういちど問題!」
「はい?」
「最初に出した問題よ。」
サクラはぶっきらぼうに言う。
「あ、『0から9までの数をすべて足すコードを書け』でした。」
慌てて答えるセキヤ。
「で、クールなコードを書く方針は?」
「えー、『フローは不要。論理をそのままコードに書き写せ。』でした。」
「そうね。じゃあ、問題に結果は含まれているかしら?」
「え?」
「あなたは、OUTPUTという結果は重要だと思うのでしょう?
で、『0から9までの数をすべて足すコードを書け』という問題の論理には結果なんてものが含まれているのかしら?」
「あ!問題の論理に、結果が含まれていることなどは絶対にありえません。問題に結果(答え、解法)が書かれてるならば、もはやそれは問題でもなんでもない単なる答えになってしまいます。」
「問題の論理にはけして存在しえないAfterの結果を重要視することは、問題の論理をそのままコードに書き写すことに繋がるのかしら?」
「いえ、完全に間違った方向性ですね。」
「ではあなたは何故そんなダサい考え方をするの?」
「よくわかりません。無意識についそう考えてしまっていました。どうもすみませんでした。」
セキヤをなじることに飽きたのか、サクラは少し調子をゆるめた。
「いい?セキヤ君。これはダサいプログラマーの悪い癖なのよ。
『フローは不要』なのに、フローを書く癖が抜けないのね。」
「結果を重視する癖が、フローを書く癖と関係あるんですか?」
「大有りよ。あなたのダサいコードのフローチャート
enter image description here
を見てみましょう。
s+n →(結果)s
であるとか
n+1 →(結果)n
であるとか、とにかくいたるところで『結果』を計算しているの。そして次のループでその『結果』を利用してまた次の『結果』を計算していて、その次も・・・というループを10回繰り返すわけよね?」
「あーおっしゃるとおりです。先輩。」
「つまり、あなたのダサいコードは、問題の論理にはありもしない『結果』を何度も何度も計算させながらループを回すというフローとして設計されているの。
念の為だけど、私のクールなやり方でも実行環境は私が感知しない、する必要もない裏で計算しているし、実行効率の問題を言ってるんじゃないわよ。コードの書き方としてダサいと言ってるの。」
「もちろんわかります。」
「こういう、問題の論理とは一切関係もなく、フローの中で何度も計算されて刻々と値が変化して、フローの制御に組み込まれてしまっているsとかnみたいな変数のことを状態変数と呼ぶの。フローがバグの元凶であるのと同様に、状態変数もバグの元凶よ。」
「たしかに、フローがあれば状態変数も必ずありますね。」

手順を分割するな、論理を分割せよ

「こういう結果を小出しながらフローを回すのは何故か?というと、ダサいプログラマーは手順を分割しようとしているからよ。」
「というと、どういうことでしょうか?」
「『様々な問題を抱えた家』があるわよね?
①匠に玄関を直させる
⇒結果(プチAfter)『なんということでしょう』。
②匠に台所を直させる
⇒結果(プチAfter)『なんということでしょう』。
③匠に階段を直させる
⇒結果(プチAfter)『なんということでしょう』。
・・・
このように、手順を分割して逐一結果を小出しながら実行するのをプログラミングだと思いこんでいるのね。だいたいこんな小出し小出しにAfterを見せられる番組なんて面白くもなんとも無いわ!」
サクラは解説しながら、またイライラしはじめたらしい。
「確かに、自分のこと考えればしっくりと来ます。というか、これ以外の考え方なんてしたことがありませんでした。僕はダサくて愚かです。」
サクラのイラツキを察したセキヤは殊更に反省した素振りを従順にアピールする。
「そうでしょうね。それに対してクールなプログラマーは論理を分割しようとしているの。もう何度も言ってるわよね?論理の最小単位である関数という論理操作を自在に組み合わせながら論理を設計していくって。」
「はい。」
「『様々な問題を抱えた家』があるわよね?
『玄関の専門的スキルをもつ匠』を設計する。
『台所の専門的スキルをもつ匠』を設計する。
『階段の専門的スキルをもつ匠』を設計する。
それぞれの匠を論理的に自在に組み上げて、最終的に『たったひとりの匠』に仕上げる。
この彼こそが『様々な問題を抱えた家』の『様々の問題』を一挙に解決する能力をもつ『万能のスーパー匠』なのよ。
ここまですべて論理操作の構成しかやっていないの。
問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくわけ。
そして、『万能のスーパー匠』を組み上げることこそが問題の論理であり、それはそのまま問題の解決になるの。
途中の過程で逐一命令する必要など皆無なのよ。
『万能のスーパー匠』はすべてやり方を知っており、その行動についてこちらはいちいち口出しない。
最後の最後に結果を一度だけみると、そのBefore(ビフォー)とAfter(アフター)の凄まじい変化を見たサザエさんの声の人が『なんということでしょう!!』って感激するのよ。」
「家族も感激して、その家庭には恒久的平和が訪れます。」
「それをやっているのが私のクールなコードなのよ。見なさい。
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //0から9までの数
  .reduce(plus); 
  //をすべて 足す
reduceplusという2つの関数が組あわされて『//をすべて足す』という関数になっているわね?
この2つの関数が、『匠』よ。
これは、それぞれ逐一実行しろ、という計算の手順書ではないわ。
2つの関数を組み合わせて、
『//をすべて足す』といういう『スーパー匠』を設計してやっているのよ。」 
論理操作の組み合わせの設計だけで、Afterという結果や、途中の計算手順も、フローも、変化し続ける状態変数も何ひとつないんですね。」
「そう。手続きを分割して結果を小出しにするループのフローを書くのではなく、論理を分割し論理だけを構成しなさい。いいわね?」
「はい。よくわかりました。」

配列をまとめてまるごと処理ができる関数

「次に、
.reduce(plus); //をすべて足す
.reduceについて説明するわ。セキヤ君、これを見て気がついたことを言ってみて。」
「まず、plusは普通に『足す』とわかったんですが、reduceという言葉の意味が不明です。普通に訳せば減らすですよね?」
「ああ、そこから来たの?これは、むしろ数学用語なの。通分する、約する、方程式を解くという意味で使われるわ。
今回のケースでは、
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //0から9までの数
をすべて足すと45というように、10個のデータが計算されてひとつに集約されるじゃない。だいたいそういう操作のイメージよ。」
「なるほど、そう言われてみると合点がいきますね。」
「それ以外は?」
「同じ関数でも.(ドット)が頭についていますね。それからplusを引数として取り入れているようです。」
「はい、まず.(ドット)が頭についていることについて説明してあげるわ。」
「お願いします。」
「そもそも、この.(ドット)は、
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //0から9までの数
  .reduce(plus); //をすべて足す
というように、[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]という配列に紐付けられているわけね。
JavaScriptの配列には、reduceというファーストクラスな関数が、最初から装備されているということなの。
つまり、このreduceはJavaScriptの配列お抱えの論理操作の最小単位なのよ。配列はぜひ、この論理で操作してください、と周到に前もって準備がなされているのね。
なぜなら、配列というものが『まとまり』だからよ。
配列という『まとまり』をまるごと処理ができる関数が用意周到に用意されているのね。
実際、[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]というのは、問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくというクールなポリシーに従って、そのままのデータまるごと用意してしまったわけね。こういうクールなやり方では、配列という『まとまり』をまるごと処理ができる関数は必要不可欠になるのよ。」
「完全に理解しました。先輩。」

『まとまり』は美しい単一の論理構造

 
「ダサいコードでは、フローの中で繰り返し処理をもって、せっかくの『まとまり』を分割してしまうの。論理を分割するのではなく、手順を分割しているのね。
いい?『まとまり』というのは、まとまっているのだから極めて単純で美しい論理構造なのよ。
『まとまり』という単一の論理構造はこれ以上分割する余地はないわ。
しかし、こういうせっかくの単一の『まとまり』という論理構造を、わざわざバラバラにしてしまって、手順を分割して、結果を小出し小出しに計算していくのがあなたのダサいコードなのね。繰り返し処理っていうのはそういうことよ。
論理構造を無視し、手順に溺れる、それがダサい繰り返し処理というフローなのよ。フローは不要。
  

他の関数を取り扱う能力をもつ関数

「次に、 .reduce(plus); //をすべて足す
というように、plusを引数として取り入れていることについて説明。
さっきみてきた通り、plusという関数は『足す』という論理の操作で、ファーストクラスの最上級の扱いだったわよね?」
「はい、論理の最小単位として自由に取り回せます。」
reduceは、配列まるごと操作できる関数だけど、同時にこういう他の、plusみたいな別の関数を取り扱う能力をもった特別な関数なのよ。」
「なるほど。だから組み合わせができたんですね。」
「こういう他の関数の取り扱う能力を備えた関数のことを特に高階関数(higher-order function)と呼ぶわ。ほんとは名前なんてどうでもいいのだけど、他の人たちもそう呼ぶので、話を合わせるために一応知っておいたほうがいいわね。」
「JavaScriptのreduceは高階関数である、と言われても今後凹むことはなさそうです。役立ちます。」
「プログラミング言語って世界共通語といえるけど、高階関数っていう名前は日本語ローカルだからどうせ外国では意味が通じないの。だからといってhigher-order functionって覚えたら今度は日本国内では通じにくいし所詮その程度のものよ。」
「こういう専門用語は日本語・英語どっちで揃えるかややこしいですね。」

高階関数の柔軟性

「でも、高階関数の存在価値って何?こんなものが世の中に存在している価値ってあるのかしら?」
「もちろんです、先輩。専門的匠の集団から万能のスーパー匠を組み上げる際などには必要不可欠だと思われます。」
「まったくそのとおりね。今回のかんたんな問題でもっと具体的に論じるとどうなるかしら?」
「今回の場合で言うなら、reduceは、『//をすべてXXする』という、まとまりへの操作として事前準備されているんですよね?
XXというのは、この高階関数が引き受ける別の関数に応じて変化して、今回は『足す』だったので、組み合わせで、『//をすべて足す』でに仕上がっていたはず!」
「だから、どういう存在価値?」
まとまりへの操作っていうのは、いろんなパターンがありえます。だからそのやりたい操作に応じて柔軟に関数を渡せばいいんじゃないでしょうか?」
「セキヤ君もなかなかやるじゃない。じゃあ『すべて足す』のではなく『すべて掛ける』場合はどうすればいいのかしら?」
『足す』能力を備えた匠を、今度は『掛ける』能力を備えた匠に入れ替えたらいいんです。だから、『掛ける』能力を備える匠をまず設計してやれば良いと思います。」
「では見せて頂戴。」
「多分、余裕です!」
セキヤは意気込んでタイプしはじめた。
var multiply = function(a, b)
{
  return a * b;
};

var s = [1, 2, 3, 4]
  .reduce(multiply);

console.log(s);
24
「いかがでしょうか?」
「合格。」

論理の最小単位としての関数のちから

「先輩、こんな風に何でも関数は組み合わせられるのでしょうか?」
「今回、たまたま高階関数というホストの存在があったから組み合わせれられたのよ。」
「ああ、そうか。」
「なんでもかんでも関数は組み合わせたら良いはずもなくて、それは個々の関数のスペック、性格によるわ。
たとえば、私自身が設計したplus関数だけど、
var plus = function(a, b)
{
  return a + b;
};
見てのとおり、これはそもそも問題の論理部品として必要な、数字を足すという操作を想定して私は書いたの。」
「そうですね。」
「たとえば、今セキヤ君が設計したmultiply関数だけど、
var multiply = function(a, b)
{
  return a * b;
};
同じように、数字を掛けるという操作を想定して書いたんでしょ?」
「たしかにそのとおりです。」
「私達が書いた関数が引数として引き受けるのは数字を想定していて、他の関数を引き受けることまではまったく想定していない。」
「高階関数は他の関数を引き受けることを想定していますね。」
reduceという配列のお抱え高階関数にしたって、あれは誰かがそういう別の関数を引き受けながら操作を実現する想定で設計して書かれたものなの。」
「一口に関数といっても、それぞれの論理を設計するときに、想定みたいなことが事前にあるんですね。」
「そう。そこまで全部含めて、問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくということなの。」
「もちろん、このplus関数だって、論理を設計するときに、別のファーストクラスな関数を引き受けてなんか処理するほうがいいのならば、そのように設計して書くのは十分アリだわ。でも今回は、そういう問題ではなかったので、数字のみを扱う関数と想定してこう書いたわけ。」
「なるほどー。先輩、いろいろ全体像がつながってきました。問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくときには、関数はファーストクラスの部品としてバラバラに取り扱えるし、部品としてのそれぞれの関数も、問題の論理に必要なように想定して設計していくんですね。関数単位のコードってなんか素晴らしいなあ。」
さくらは笑みを浮かべる。
「フローを設計するよりも、よほどクールでしょ。これが論理を設計するということなの。」
「関数という論理の最小単位の部品をもってすれば、問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくことは容易いような感じがしてきました。」
「なんでもできるわ。これは本当にクールな方法なのよ。」
「でも『神の眼』のスキルとしてはまだレベル0なんですよね?」
「レベル0ね。でも今日セキヤ君はいちばん大事なキモの部分を学んだのよ。」
「ありがとうございます。サクラ先輩。」
「あ、ちなみに、この関数という論理の最小単位で、問題の論理そのものをコードに単純に書き写すことを徹底的にやっていくクールな方法をいちおう世間では関数型プログラミングって呼んでいるみたいだわ。」
「名前なんてどうでもいいけど、他の人たちもそう呼ぶので、知っておけば話が合わせられるし、凹むこともないですね。僕が欲しいのはあくまで『神の眼』のスキルです!」
「まだレベル0だから頑張りなさい。もう今日は遅いからここまでにするわ。」
「サクラ先輩、今日はどうもありがとうございました!」
「はい。早くPCの電源落としなさい。さっさと帰るわよ。」
これから数日間、サクラとこんな濃密な時間が共に過ごせるのだ、そう思うとセキヤは再びこう考えるしか無かった。
この高校に入学できてよかった。コンピュータ部に入部して正解だった。今、この瞬間、僕は青春を謳歌している!セキヤは自分の幸運と幸福を噛み締めていた。

Day2 言語の限界を超越する 『神の眼』レベル1

0から999までの数をすべて足すコードを書け

『神の眼』のスキル レベル0の限界に到達

言語の限界を乗り越える方法

『神の眼』のスキル レベル1

まとまりを操作していく世界

filter 関数

CSSとjQueryという宣言型パラダイム

フローが消えると論理構造だけが現れるチェーン構造

なぜフローだったのか?

なぜコードは上から下へ流れていくのか?フローチャート
命令形(手続き型)プログラミングの歴史とフロー制御

何がコードを駆りたてるのか?イベント駆動の世界

  
「宣言型プログラミング」と「イベント駆動」「オブジェクト指向」
 

オブジェクト指向のダサさ 論理とオブジェクトとメソッド

『神の眼』の頂点へ

node.js GitHub ATOMエディタなど

Day3 純粋な論理の世界へ 『神の眼』レベル2

0から9までの数をすべて足すコードを書けpart2

自然数という無限数列

『神の眼』のスキル レベル1の限界に到達

論理の世界と物質世界の間 コンピュータと計算の正体

論理と計算手順の明確な分離をする必要性

必要な時に必要な分だけ計算する!たったひとつの冴えたやり方 Call By Need = 遅延評価戦略

『神の眼』のスキル レベル2

無限の世界を見通す『神の眼』

フィボナッチ数列

破壊と副作用のない参照透過な純粋な数学世界へ

Day4 神の視点で宇宙を見通す『神の眼』レベル3【最終レベル】

入出力の問題

時間の問題

『神の眼』のスキル レベル2の限界に到達

時間の論理化

関数・リアクティブ・プログラミング(FRP)

『神の眼』のスキル レベル3 最終レベル

神の視点で物質世界のすべてを見通す

時間と空間を超越する純粋論理の世界

タイマーコードなど

Day5 ???

その他諸々情報を補足したりします。

~エピローグ~


ちなみに筆者はMではなくSです。
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.
This page has been shared 6 times. View these Tweets.
0
kenokabe
kenokabe
1665Contribution
Follow
人気の投稿
SECURITY WARNING: Please treat the URL above as you would your password and do not share it with anyone. See the Facebook Help Center for more information.
SECURITY WARNING: Please treat the URL above as you would your password and do not share it with anyone. See the Facebook Help Center for more information.