Rubyのtimesが羨ましかったので
この前、@alea12とぜろゆ嬢と3人で焼肉行ってた時に、Gaucheで10回Hello,worldを表示するのを見せたら、Rubyのコードでドヤ顔されて悔しかったのでRubyのtimesっぽい書き方ができないか試してみた話。
見せたのは以下のコード。
(for-each (lambda (x) (print "Hello, world")) (iota 10))
なんか、Hello,worldって10回出力するだけなのに、直感的じゃない?個人的には少し微妙。
以下、やってみた
お題「10回標準出力にHello, worldを出力する」
Rubyでループのやり方の例を出すときによくやるアレですね。
10.times do p "Hello, world" end
短いし、素直で読みやすいね!
Rubyのこういう部分好きだなーって思う。だけど、Schemeは前置記法という呪いに囚われているので、演算子がリストの先頭にこなければならない。
だから、(10 times (print "Hello, world"))って書けない。*1
for-eachを使うと自然じゃない(気がする)。かと言って、dotimesも微妙な気がする。
(dotimes (i 10) (print "Hello, world"))
読めるし、分かりやすいんだけど、どうしてもiが邪魔に思える。これ、どうしたらええねん…。
ということで、ちょっとGaucheの裏テク?を使ってみた。
結果こんな書き方が出来るようになった。
(10 :times (lambda (x) (print "Hello, world")))
lambdaが嫌いなら、ハットに変えてこう。
(10 :times (^x (print "Hello, wolrd")))
まぁまぁじゃない??ダメ??
タネ明かしすると、こういうメソッドを足していました。(なんとなく察している人はいるんだろうけど)
(use util.match) (define-method object-apply ((i <integer>) (k <keyword>) (p <procedure>)) (match k (times (map p (iota i)))))
mapにするかfor-eachにするかはちょっと悩んだけど、mapの方がリスト返ってくるし汎用性高そうだったからとりあえずmapにしてみた。
(その代わり、printの#
真ん中のtimesって、キーワード変えて違う処理増やしたいと思った時に便利そうだなーって思ったからあるだけで、実際にはキーワード部分は取り除いても支障はなく、むしろキーワードを取り除くと以下のように非常にシンプルになる(笑)
(10 (^x (print "Hello, world")))
そもそもSchemeで「10回繰り返す」なんて明示的に数を指定することって、ないよなー…って思ったwあと仮に書けてもそんなに嬉しくはないなーってw
以上!
追記
まさかまさかのshiroさんのIsland Lifeでこの記事が取り上げられた上にdotimesの書き方に変数の省略記法まで追加してもらえた!!
今、GaucheのHEADならこう書ける。
(dotimes (10) (print "Hello, world"))
変数の省略できたら良いのになーとは考えてたけど、まさかGaucheの方でそう書けるようにしてくれるなんて思ってもいなかった><
これだと凄くシンプルに見えるので、変なことしなくて良さそう!
*1:こう書くと「書けなくもないよ!マクロ使えば!」とか言われそうなので安易に言えないのだけど。マクロよく分かってないし