バリケンのRuby日記 RSSフィード

2008-03-23

[] 関数と特殊形式  関数と特殊形式 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  関数と特殊形式 - バリケンのRuby日記  関数と特殊形式 - バリケンのRuby日記 のブックマークコメント

今日は、関数と特殊形式(special form)の違いについて勉強するよ。

関数と特殊形式の違い

関数と特殊形式の違いは、「引数が全て評価されるのが関数、全ての引数が評価されるとは限らないのが特殊形式」という理解でいいみたい。

じゃあ「全ての引数が評価されるとは限らない」ということを調べるため、特殊形式のandとorの挙動をみてみよう!こういうことを調べるには、副作用(side effects)のある関数を使うのがいいよね。print関数は、与えられた引数をそのまま返すだけじゃなくて、標準出力にも出力するよ。

[1]> (print "Hello, world!")

"Hello, world!"
"Hello, world!"
[2]>

"Hello, world!"がふたつ表示されてちょっとややこしいけど、最初の"Hello, world!"が標準出力の出力結果、ふたつめの"Hello, world!"が戻り値を表しているよ。戻り値がややこしいなら、ブロックを表すprognを使って戻り値をnilにしてみればわかりやすいかな?prognは、与えられた引数を順に評価していくよ。

[2]> (progn (print "Hello, world!") nil)

"Hello, world!"
NIL
[3]>

こんどは最初の"Hello, world!"が標準出力の出力結果、ふたつめのNILnilの戻り値(prognは一番最後の評価の戻り値を返す)なので、ちゃんと副作用が発生しているのがわかるよね。

だいぶ横道にそれちゃったけど、いよいよandとorの挙動を見てみるよ。まずはorから

[3]> (or t (print "Hello, world!"))
T
[4]>

画面(標準出力)には"Hello, world!"が表示されなかったね。確かに、論理和の定義から一番最初の引数がtなので戻り値はtに確定するから、残りの引数「(print "Hello, world!")」は評価されなかったみたい。

つぎに、andも試してみるよ。

[4]> (and (print "Hello1") nil (print "Hello2"))

"Hello1"
NIL
[5]>

画面(標準出力)には"Hello1"だけが表示されて、"Hello2"が表示されなかったね。これは第一引数「(print "Hello1")」は評価されたけど、論理積の定義から第二引数がnilなので戻り値がnilに確定してしまったので、第三引数「(print "Hello2")」は評価されなかった、ということだね。

ちなみに、さっき出てきたprognも特殊形式みたい。たとえばcatchthrowを使って、途中でprognのブロックから脱出するようなこともできるよ。

[5]> (catch 'hoge
  (progn
    (print "Hello1")
    (throw 'hoge t)
    (print "Hello2")))

"Hello1"
T
[6]>

prognが関数なら引数はすべて評価されるから"Hello2"も画面(標準出力)に表示されるはずだよね。でも、prognは特殊形式だから、(throw 'hoge t)でprognのブロックから脱出してしまったあとの引数は評価されなかったんだね。

こんな感じで、関数と特殊形式の違いは「引数が全て評価されるのが関数、全ての引数が評価されるとは限らないのが特殊形式」だということがわかったよ。

[] Rubyでも実験  Rubyでも実験 - バリケンのRuby日記 を含むブックマーク はてなブックマーク -  Rubyでも実験 - バリケンのRuby日記  Rubyでも実験 - バリケンのRuby日記 のブックマークコメント

今日Lispで試したことを、Rubyでも試してみるよ。

まずはorから。Rubyだと「||」がorの意味だったよね。こういう実験にはirbが便利。

irb(main):001:0> true || puts("Hello, world!")
=> true
irb(main):002:0>

たしかに、さいしょのtrueでこの式はtrue確定だから、次の「puts("Hello, world!")」は評価されなかったね。

次は、andだよ。Rubyだと「&&」がandの意味だったよね。

irb(main):002:0> puts("Hello1") && false && puts("Hello2")
Hello1
=> nil
irb(main):003:0>

こんども期待通り、さいしょの「puts("Hello1")」は評価されたけど、次のfalseでこの式はfalse確定だから、最後の「puts("Hello2")」は評価されなかったね。

最後に、catchthrowだよ。

irb(main):003:0> catch(:hoge) do
irb(main):004:1*   puts("Hello1")
irb(main):005:1>   throw(:hoge)
irb(main):006:1>   puts("Hello2")
irb(main):007:1> end
Hello1
=> nil
irb(main):008:0>

やっぱり期待通り、throwでブロックを脱出したあとの「puts("Hello2")」は評価されなかったね。

Rubyのブロック(do ... end)が、Lispのprognに相当するんだね。あとRubycatchthrowは、Lisp由来なのかな?

トラックバック - http://rubyist.g.hatena.ne.jp/muscovyduck/20080323