2015/06/22

FoldLeft

FoldLeftという考え方について。
(関数型プログラミングについて初学者が錯綜しているので話半分で)

例によって1から9までの数字を足した結果を求めよ、というプログラムの話になるんだけど
とあるScala講義(どこかの大学かな)でこんな話をしていた。

■この教室にいる人全員の年齢を足した結果を求めたい

一つのやり方は、まず1枚の紙を用意して端の人に渡す。その人は年齢を書いて次の人に渡す。
次の人は紙の数字を、自分の年齢を足した数に書き直して、また次の人に渡す。(鉛筆と消しゴムを使うんだろう)。
これを終わりの人まで繰り返す。最後の人は紙を教師に渡して完成。
このやり方は、totalという変数を用意し、forループ内でどんどん数を足していく従来のプログラミングとおんなじだ。

もう一つのやり方は、(全員が紙を持ってるとして)最初の人は自分の年齢を書いた紙を次の人に渡す。
次の人はその数と自分の年齢を足した数を別の紙に書いて次の人に渡す。 これを終わりまで繰り返す。
これで完成で良いんだけど、より正確には最後の人は自分の年齢を足した数を書いた紙を前の人に戻す。今度はどんどん前の人に戻して最初の人まで戻ったら教師に渡すというやり方にする(ちょうど最後の人は教室の後ろの席だとすればなんとなくわかる)。
どんどん新しい紙にするという部分が最初のやり方と違う。
プログラミングで考えると、totalという変数に当たるものが無い。
最初の人から最後の人まで繰り返すという言葉に反して、このやり方をループで書くのはしんどい(紙を変数に見立てた場合)。

これを再現するのが関数型の書き方で再帰を使った書き方になる。以下はJavaScriptでの実装例(だと思う)。

var list = [1,2,3,4,5,6,7,8,9]

var f = function(val, list){
  if (list.length === 0) {
    return val
  } else { 
    var e = val + list.shift()
    return f(e, list) 
  }
}
console.log(f(0, list)) 

うーん、ちょっと何やってるか分かんないと思う。どうせうまく説明できないが説明すると、fという関数は初期値0と数値のリストを渡される。
1. リストが空なら第一引数のvalをそのまま返す。
2. リスト内に数値があれば先頭の数を足し、その数と残りのリストでもう一度fを呼び出す。
3. 残りのリスト内にまだ数値があれば先頭の数を足して、その数と残りのリストでまたfを呼び出す。
...
x. 最後まで行くと全部足した数と空のリストでfが呼び出されるので、全部足した数が返される。
再帰なので最後まで行くと一気に最初の関数呼び出しのとこまで戻されることになる。

list.shift() というのが微妙なとこだ。
リストの先頭の要素を取り出す関数だが、取り出すと本当にリストから削除される、popのような関数だ(popは後入れ先出しなのでそこが異なる、どっちかというとQueueかな?)。
var e = val + list.shift() だけで先頭要素と足し合わせるのと、先頭を除いた残りのリスト、というのをやっている。
これなら順次新しい紙を用意して足し算し、次の人(残りの人)に渡すというようなやり方に沿ったプログラムになってると思う。

この順々にやる方法がFoldLeftという考え方だ(もしかしてFoldRightかも)。
リスト先頭の要素と、何の処理をするか(今回は足し算)と、リストの残りは再帰でやる、という戦略。

せっかくなので全部書こうと思う。
何の処理をするか、というfunctionをfに渡すように作ると汎用的になる。また、リストの先頭要素と残りのリストをそれぞれ分かりやすくlist_headとlist_tailとした。
var list = [1,2,3,4,5,6,7,8,9]

var f = function(val, list, f2) {
  if (list.length === 0) {
    return val
  } else { 
    var list_head = list[0]
    var list_tail = list.slice(1)
    var e = f2(val, list_head)
    return f(e, list_tail, f2) 
  }
}

var plus = function(a,b) {
  return a+b
}

console.log(f(0, list, plus))
となる。リスト内の要素を全部掛け合わせた結果が欲しいなら、
var product = function(a,b) {
  return a*b
}

console.log(f(1, list, product))
となる(最初の値は1に変わる)。
この辺まで考えると、リスト内の数値を全部足した結果を返す、全部掛けた結果を返す、という専用の関数を作りたくなるはず。
var f = function(val, list, f2) {
  if (list.length === 0) {
    return val
  } else { 
    var list_head = list[0]
    var list_tail = list.slice(1)
    var e = f2(val, list_head)
    return f(e, list_tail, f2) 
  }
}

var plus_all = function(list) {
  var plus = function(a,b) {
    return a+b
  }
  return f(0, list, plus)
}

var product_all = function(list) {
  var product = function(a,b) {
    return a*b
  }
  return f(1, list, product)
}

var list = [1,2,3,4,5,6,7,8,9]
console.log(plus_all(list))
console.log(product_all(list))
FireBug等で動くのが分かるだろう。関数型プログラミングになってるはずだ。どんなもんだろう。
plus とか product が関数の中で定義されてるのがちょっとこざかしい感じがする。

関係ないが今年度の新人研修に関わることになって、プログラム言語研修に何の言語を使うかが議論となった。今まではC言語だったのが、Cは古い、もっとプログラミングそのものを教えるのに適した言語があるんじゃないか、という話になり、JavaScriptで研修を行うことになった。
その方針だけ決めて講師は社内のエンジニアを適当にアサインしたのだが、どうも比較演算子には「==」と「===」があるとか、「1 === "1"」はFalseだが「1 == "1"」はTrueになるという内容を教えたらしい。また、JavaScriptには型が無く、数値の足し算のつもりが文字列の連結になるぞ、というようなことを教えたらしい…。
JavaScirptを選択した主旨が伝わってないじゃん。そんな言語仕様ばっかり教えるならCを教えた方がまだ良かったんではないかと。
JavaScriptを教えたいんじゃなく、プログラミングを教えたいだけだったんだけどな。

純粋にプログラムを教えるために余計な言語仕様にとらわれない言語としてJavaScriptって個人的には悪くないと思った。BestとかExcellentではないけどCやJavaと比べれば、Not badと思ったんだけどな。でも初心者を打ちのめすのに効果絶大な言語仕様も多いから、講師のセンスだな。
手続き型としてはコールバックが多用されるのが難しいというのはあると思う。

2015/06/02

ise

伊勢参り感想

金曜に休みを取って金土日と伊勢に行ってきた。
伊勢というか、名古屋、伊勢、二見浦、かな。名古屋観光は新幹線が高額なこと以外は簡単に行き来できるのでいい気分転換になりそう。
名古屋駅から名古屋城まで歩くと結構疲れる。スーツケースはロッカーに預けとこう。
途中で自社の名古屋事業所を外から眺めてみる。まぁ社員証も無いし知り合いもいないと思うので見るだけ。
名古屋城は入るのにお金取る。以前に中入ってみたがそれほど面白くなかったので外側だけ見て帰りの手段を探す。

名古屋城前のバス停に名古屋メーグルというふざけた名前のバスが来てた。名古屋駅も行くようなのでよく確認せずに飛び乗る。しかし名古屋駅から離れる方向のに乗ってしまい、ぐるっと回って名古屋駅まで行くのに1時間ほどかかることが判明。名古屋城から名古屋駅、という方向のメーグルもあるので乗るときは確認するといい。
メーグルは名古屋の観光名所がバス停になってるので降りずに1時間乗るのも楽しいかもしれないし、1日パスで観光地巡りも出来るようになっている。しかし待ち合わせの時間が近いので地下鉄ですぐ名古屋駅に戻れる名古屋テレビ塔で降りた。名古屋テレビ塔の地下街をぐるっと歩く。
札幌の大通りのテレビ搭と似ていて良い。就活のときに一度来たのを思い出した。

名古屋から伊勢市へ

なんと近鉄線で1時間半以上、かかるという。で普通の通勤電車の帰宅ラッシュに当たった模様。1時間くらい混雑したなかで立っていて辛かった。
しかし地元の人に混じって移動をすると旅情を感じる。その前の名古屋テレビ塔から名古屋駅までなどの移動でも、地元の高校生を見たりした。そして彼らは自分とは全く関わらない世界軸に生きてるんだろうなぁなどと思いをはせる。
彼らにとっては何でもない毎日繰り返される日常なのだろうが私にとっては一生のうちでそう何度も無いであろう名古屋旅行の一部分なのだ。
宗教観、輪廻と旅情、何千年と続くこの広い地球上、何百億といた人間の中で、たとえばスクランブル交差点で一瞬すれ違うだけという人であってもすごく強い縁で繋がっている、などと美輪明宏は言っていたっけ。袖振り合うも多生の縁?
まぁそんなことを思ったり思わなかったり。
で、伊勢市へ着く。
日本で最も有名な神社がある街としてはあまりに人が少なく静かだった。金曜だからこんなに静かだが明日は街中参拝客で溢れるはずだ、などと話をする。
その夜はその土地の居酒屋で何故かもつ鍋を食べる。うまかったけど。
泊まったホテル、というかウィークリーマンションには蚊がいた。刺されたかどうかは謎、かゆいのは間違いないがもう布団のせいでかゆいのか、蚊に刺されたのかわかんない。

伊勢神宮参り

伊勢市駅にもロッカーがあるし、駅近くに参拝者向けのお荷物預かり所もあるので旅行鞄は預けて行く。
せっかくなのでと正式なお参り順序で行く。外宮(げくう)までまず歩いてお参りする。
しかしなんと財布には小銭が1円玉一枚しかなかった。途中の自販機で脱水防止にお茶を買ったのだが、そのとき「お、丁度160円あるぞ」などと思って小銭を一掃してしまったのだ。
仕方ないので途中の神社はお辞儀だけして、一番奥の本殿に1円玉をコロンと投げ落とす。外宮とは言えさすがに多くの参拝客がいた。
でバスに乗り内宮(ないくう)へ。バスの中でちゃんと両替して10円玉を量産する。
内宮の前に猿田彦神社へ寄ってお参り。猿田彦神社から内宮は歩いてすぐのようだ。
内宮の前には”おかげ横丁”というお土産屋の通りがある。
これが古い建物の雰囲気を残していてゆっくり回りたくなるのだ。まぁお参りの後で回るとして、内宮の中へ。
本殿に向かう途中に何本も杉の木が生えていたが、一本ずつ全部が御神木のような立派な木だった。
とにかく大きな森の中に大きな神社がある、という感じ。神社もそうだがその土地全部がなんかありがたい雰囲気。そして外宮とは比べ物にならないくらいたくさん人がいた。
まぁ実は神社よりおかげ横丁の方が面白くて人を寄せ付けてそうなんだけど。。
その中の赤福本店で赤福を食べた。うまい。
お土産屋も様々な種類があり、食べ物屋も松阪牛だの伊勢うどんだのあり、良い。
いちいち建物が古い雰囲気だし。そしてたくさんの観光客の中、燕もたくさん飛び回っており、あちこちに燕の巣があった。燕には二見浦でも驚かされる。
さて伊勢神宮には人や燕が沢山いたが、実は蚊も沢山いたようだ。。この日6箇所も刺された。そしてなかなかの毒性の強さ。白黒のやつじゃないから大丈夫かと思ったが。ダメな痒さだった。

二見浦へ

伊勢参りの後二見浦へ移動。ちょっと良い宿というのに泊まる。古い和風旅館だが清潔感もあり、人も少なく、たしかに良い雰囲気。正直二見浦には何もないんだが、それもまた良い。
なんかぼーーーっと過ごす。
ふらっと海岸線を歩き、その土地の名物という塩羊羹の本店に行った。すっごい古いお店で、店内に燕の巣があった。燕の子が5匹並んでうごめいてる姿は若干気持ち悪かったが(蓮の実的なキモさ・・)、燕の巣があるんですねーすごいですねーなどと愛想を言う。でそこで塩羊羹を一本買う。おまけでカエルの小さな置物を貰ってしまった。

翌日、夫婦岩?というのを見学し、伊勢から無事に帰れるようにという古人の願いのこもったカエルの置物を眺めて歩く。そこらじゅうカエルの置物だらけ。
これだけ神社参りをしてるのに、おみくじを一個も買ってないので最後に夫婦岩のところの神社で”カエルみくじ”というのを引いてみる。カエルの口に手を突っ込んでゲコゲコと呟きつつ口の中一番奥のおみくじを引っ張り出す。で、「吉」という普通のやつ。いっつもこれだ。
そういえば夫婦岩はすごく風が強くておみくじ飛ばされそうだなーなどと思っていると、なんと千円札が何枚か飛んできた。それを追いかけるおばちゃんも走ってきた。で千円札が風に乗って3枚とも僕の手元に。もちろんおばちゃんに返したが、もしやあれは金運Upだったのか?
金運といえば夫婦岩の近くには非常に商魂たくましいお土産屋があるので注意が必要だ。必死過ぎてちょっと怖い。二見シーパラダイスというお土産屋ショップの店員もけっこう怖いくらい売り込んでくる。
今後二見浦へ観光に行く人へのアドバイスだが、もしお土産屋の店員が少しでも怖いと思ったら無言で逃げていいと思う。追いかけては来ないので。

帰りは二見浦から名古屋、名古屋から東京、とどんどん都会になっていく。 カエルのご利益か、なにも問題なく帰れた。
実はカエルなんか拝まなくとも無事に帰れただろうが、もしかしたらカエルを拝まなかったら無事では済まなかったかもしれない。それは分からない。こうやって信仰心が芽生えるのは危険だ。カエルを拝んだから無事に帰れた。カエルを拝んでも無事に帰れなかったとしたら信仰心が足りなかったからだ。などとなる。
こういう信仰心、ジンクスが芽生えそうな危険を感じたらあえて崩してみることが必要だ。カエルの置物を蹴っ飛ばして、ちゃんと帰り着く、とかね。
まぁカエルがかわいそうだからそんなことしないけど。

motion sickness

何故か名古屋に向かっている新幹線の中で、新しいモバイルノーパちゃんを持ち出して、華麗にプログラミングでも、と思ったが。
自分は特に乗り物に弱いとは思わないんだけど、PCつけて作業はヤバい。
東京で乗り込み、品川を超えたあたりですでにちょっと乗り物酔い。
ちなみに新幹線の中でネットが繋がるのはU-mobileの格安Simを刺したL-01eでテザリングしているからだ。なんか新幹線もWiFiがあったような話を聞いたような気がしなくもない。しかしよくわからんのでいつも通りスマホのテザーを使う。L-01eにU-mobileを刺してテザリングするためにはroot化して内部設定を変えないといけない。前に四苦八苦してやったがすでにやり方を忘れてしまっている。
新横浜でグロッキー。。