(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

Rubyのtimesが羨ましかったので

この前、@とぜろゆ嬢と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:こう書くと「書けなくもないよ!マクロ使えば!」とか言われそうなので安易に言えないのだけど。マクロよく分かってないし