読者です 読者をやめる 読者になる 読者になる

続・パスカルの三角形

というわけで、昨日話題を振ったパスカルの三角形の Enumerable 活用版です。

ruby -e '(0..9).map {|k| (1..k).inject([1]) { |a,dummy| (a + [0]).inject([0,[]])
 { |(prev, li),x| [x, li + [prev + x]] }[1] } }.each {|l| p l}'

もしくは、中間結果をきちんと最後まで持ってくるようにして、

ruby -e '(1..9).inject([[1]]) { |a,dummy| a << (a[-1] + [0]).inject([0,[]])
 { |(prev, li),x| [x, li + [prev + x]] }[1] }.each {|l| p l}'

実行結果は

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]

畳込み関数 (inject) は偉大なり。

今回は畳込みは右か左かはあんまり関係ないのですが、a[-1] + [0] とかやっているのは、本当のリスト処理の思想からするとダメダメですね……。

シンプルさを求めるなら

ruby -e '(0..9).map {|k| (1..k).inject([1])
 { |a, dummy| ([0] + a).zip(a + [0]).map {|x, y| x + y} } }.each {|l| p l}'

リストの末尾に concat するのがお嫌いなら、

ruby -e '(0..9).map {|k| (1..k).inject([1])
 { |a, dummy| ([0] + a).zip(a).map {|x, y| y ? x + y : x} } }.each {|l| p l}'

挫折した方法

 [1, 2, 1] -> [1, 1, 2, 2, 1, 1] -> [0, 1, 1, 2, 2, 1, 1, 0]
 -> [0+1, 1+2, 2+1, 1+0] -> [1, 3, 3, 1]

とやってみたかったのですが、Ruby の one-liner でエレガントに書く方法が分からず挫折しました……。
増やすところまでは map して flatten とか使えばいけるんですけど、まとめるほうが難しい。

(追記)Ruby ならではの素晴らしい解決法は こちら
(追記その2)ブロックの残りの引き数を受け取る * を使って実現した方法は こちら