intime o'

たとえ自分が創りだした世界がどんなものだろうと、その世界で賢い判断を下せ (アダム・ファウワー著・矢口誠訳「数学的にありえない」・下より)
FizzBuzz 2012/08/22(Wed.)
 FizzBuzzを書く.
但し次の3つの制約を課す.それはつまり、
・ループを使わない
・剰余を使わない
・条件分岐を使わない.


ぜひこの出題者に直接色々問い詰めたいのだが、誰かがこの問題について
考えているのを横から見ていたにすぎないので、私はその出題者を知らない.

当然ながら、「ループを使わない」とは別の何かを使ってループを再現する
のだろう.だとすればその再現方法はループとは呼ばないのだろうか.
呼ぶのだとしたら、この問題は手に負えなくなるので、呼ばない、とする
のだろう.だとしたらどの程度まで?
一番やっかいなのが3つ目の条件「条件分岐を使わない」だろうけど、
条件分岐を例えば
if (cond) {
    body
} else {
    alt
}
なのだとしたら
cond ? body : alt
これは条件分岐?
今思いついたが
if (cond) body;cond && body
などと書けるのだった.取り敢えず気づかなくて以下の様なコードを書いた.

#Python
j=lambda n,m:n in map(lambda x:x*m, range(1,n))
def l(n):
    print(["Fizz","Buzz","FizzBuzz",n][{True:2, False:{True:0,False:{True:1,False:3}[j(n,5)]}[j(n,3)]}[j(n,15)]])
map(l,range(1,30))

コレ以上短くできなくて、javascriptにしてみた.
// js
range=function(i,y){return {true:function(){return [i].concat(range(i+1,y))},false:function(){return [i]}}[i<y]()}
j=function(n,m){return!!~range(1,n).map(function(x){return x*m}).indexOf(n)}
range(1,30).map(function(n){console.log([a="Fizz",b="Buzz",a+b,n][{true:2,false:{true:0,false:{true:1,false:3}[j(n,5)]}[j(n,3)]}[j(n,15)]])})

ループを使わない以上再帰か何かを書くのだが、その度にfunctionと書くので
結局余計に長くなった.またpythonでは[1..10]をrange(1,10+1)などと書ける
が、jsにはそのようなものはなく、自力で書いてしまった.この中でも条件
分岐を使わないことにしたのでさらにさらに長い

さらにさらに、pythonでは Arrayがある要素を含めるかどうかを判定する述語
として x in array があるが、 javascriptではそのようなものは聞いたこと
がなく、node.jsでダメ元で試したら全く同様に文法でそのようなものがあったが、
これはキーに就いての判定だったので、今は不要.キーというのは
ここでは配列なので、[1,2,3]のキーは 0,1,2 である.
> 2 in [0,1]
false
> 2 in [0,1,1]
true
結局、何番目に含むかを返すindexOfの返り値を強引にbooleanにした.
!!~array.indexOf(x)

8/25 追記 Array#some() というものがあった. 引数はfuctionで、 array.some(function(a){return x==a}) などと 使う.余計長い.
それぞれの制約に対する解法を書くと、 ループはもちろん再帰でもいいが、再帰をすると終わらせる為の基底条件が 必須であり、そこで条件分岐を使う.今はできるだけ使いたくないので mapを使用した.つまり for (i in array) do(i)array.map(function(i){return do(i)}) に等しい 剰余を使ってはいけないというのは剰余を求める演算子、つまり たいていの言語で % か mod なんてので定義されてるものを使っては いけないと解釈.再帰すれば function mod (n, m) { return n<m ? n : mod(n-m,m); } これは n、mが生の整数なら正しく動く、がまた条件分岐である. このくらいなら条件分岐でも良かったかもだけど、私はこうした. n `elem` map [1 .. n] (\x → x * m) つまり、mの倍数のリストを作ってその中にnが含まれるかを判定する. リストは無限列である必要はなく、nもmも1以上ならば、n*mを最大と しておけば十分である. 最後に条件分岐を使ってはいけないという制約. これはオブジェクト指向な部分を利用した. つまり、true, false というbooleanを組み込みに持つjavascriptで、 そのtrue,falseをキーとするオブジェクト(辞書型変数)を作ってしまう. var obj = { true : "#t", false : "#f" }; 通常、true,falseの部分は予約語を使うべきでないように思えるが、 実際 obj[true], obj[false]でそれぞれの要素にアクセスできる. そして大事なこととして、キーに述語を入れてしまって alert(obj[1 == 1]); alert(obj[3 < 0]); これは嬉しい予想どおりに動く. さてショートコーディングにおいて条件分岐は三項演算子 (?:) で書くのが 基本であるが、また次のような文法が利用できることもある、というのを 今思い出した.つまり if (cond) body;cond && body に等しく if (! cond) alt;cond || alt に等しい、 これは論理演算であるが、ifの非常に重要な性質を満たしており、 前に私の書いた true, false をキーとするオブジェクトはその性質 を持っていない(初めjavascriptのコードで私はオブジェクトの要素を 全てラムダ式に包んでいる). if (cond) A else B; は Aが実行(評価)されるならば、Bは実行(評価)されず、逆もしかりである. これは言語の評価戦略などに関わらず(もしかしたらそういう言語もあるかも だけど)満たされる性質であり、評価戦略という言葉を出したのだから 言ってしまうと遅延評価のように見える. 例えば cond || alt は cond と alt の or を取るのだが、condがtrueならば、altはfalseでも trueでもどちらでも(cond||alt)はtrueを返す、という性質を処理系の 設計者がわかっていれば、altはそもそも評価されない.そして大抵の 処理系ではそうなっているだろう. else を伴うifを書きたい時、次を考える. (cond&&body)||body condははじめに一度だけ必ず評価される.falseなら(cond .. body)がfalseなので ||の後ろが実行される.trueならbodyを評価し、そしていまここで bodyが常にtrueを返す とする時、||の後ろは実行されない. これは通常の if .. else .. だ. 逆に (cond || alt) && body これは altが常にfalseを返す 時に正しく機能する. またそのbody,altの返り値を考えたくないならばカンマで並べて (cond .. body) , (cond .. alt) としてもよいが、condは二度評価される. で、これを使えばもっと短く書けるのかな?

コメ(0) | トラ(0)