黒魔術に手を染めてしまったかもしれない。

再帰の技法という本にある問題で、こんな感じの出力を表示する問題があった。*1

bo     b1     b2     (b0 or (b1 and (not b2)))
----------------------------------------
false  false  false  false     
false  false  true   false     
false  true   false  true      
false  true   true   false     
true   false  false  true      
true   false  true   true      
true   true   false  true      
true   true   true   true      

これをrubyで楽にできないかなーと思った。
「要素がb0,b1,b2の配列」を要素として持つ配列がつくれたら、あとは簡単。

要素の配列の要素数が2つのとき(b0,b1まで)

bool=[false,true]
p bool.map{|a| bool.map{|b| [a,b]}}.inject{|a,b| a+b}

#=>[[false, false], [false, true], [true, false], [true, true]]

要素の配列の要素数がnつのとき(b0,...b(n-1)まで)

いちいちたくさん書くのは面倒。
どうしたら簡単にできるんだろ?*2
式自体を文字列として作っちゃえばいいのかもしれない

def make_list(seed,n) 
  symbols=("a".."z").to_a[1..n]

  x=symbols.inject(["",""]) do |result, element|
    result[0].concat("seed.map{ |#{element}| ")
    result[1].concat("} ")
    result
  end.join("[#{symbols.join(", ")}]")
  eval(x+%Q!#{eval("'.inject{|aa,bb| aa+bb}'*(n-1)")}!)
end

## 表示用 ######
def prepare
  printf("%-6s %-6s %-6s %-10s\n",:bo,:b1,:b2,"(b0 or (b1 and (not b2)))")
  puts "-"*40
end

prepare
make_list([false,true],3).each do |b|
  printf("%-6s %-6s %-6s %-10s\n",b[0], b[1], b[2], b[0]||(b[1]&&(not b[2])))
end

eval使いまくり…

追記

こうすれば、evalを使わなくてもすんだ。

def f xs,ys
  xs.map{|x|ys.map{|y|[x,y]}}.inject{|aa,bb| aa+bb}
end
def make_list2(seed,n)
  Array.new(2,seed).inject{|re,e| f(re,e)}.each{|ee| ee.flatten!}
end

*1:実際には、false=>F true=>T でした

*2:schemeでは同じ関数をn回合成してそれを適用という感じでできた