2015年12月6日日曜日

BlockClosure の引数の数に対してことなる引数の数を与えてよしなに呼び出す

Smalltalk Advent Calendar 2015 - Qiita の 12月5日の内容です。
Smalltalk のブロッククロージャについて書きます。
このエントリで使用している Smalltalk は Pharo Smalltalk ですが、他の処理系でもだいたい同じのはずです。

BlockClosure をみてみる

Smalltalk では [ と ] で囲まれている部分がブロッククロージャとなります。



[ ] を inspect it してみました。
BlockClosure の詳細をみたい場合はどうすればいいんでしょう。っていうときに、 Smalltalk が他の言語と大きく異なる動的な評価という特徴を生かすことができます。

BlockClosure のインスタンスに対して browse というメッセージを送ってみましょう。


BlockClosure のインスタンスを inspect している下のペインで self browse と打ち込んで do it します。

システムブラウザに BlockClosure を表示することができました。ラクチン。

BlockClosure に引数を与えてみる

Ruby のブロックのように BlockClosure も引数を受け取ることができます。
たとえば、ふたつの引数を受け取って、ふたつの引数を Transcript に出力してみましょう。

BlockClosure にふたつの引数を設けて、ふたつの引数を与えるには BlockClosure でふたつの引数を受け取ることを明示的に記述して、BlockClosure>>#value:value: を使います。

出力できました。

BlockClosure が受け取る引数の数とはことなる数の引数を与えてみる

ちょっと意地悪をしてみましょう。
ふたつの引数を受け取る BlockClosure に BlockClosure>>#value: でひとつの引数を与えるとどうなるんでしょうか?
ありゃりゃ、 Error が発生してしまいました。
Ruby ではこういった意地悪をするとふたつめの引数には nil が入って例外が起こらないという挙動をするので、わりと適当に書きたいときは便利な挙動だったりします。
% ruby -e '[1,2,3].each{|i,j| p i; p j}'
1
nil
2
nil
3
nil
うーん、厳密なのもよいけれど、柔軟に受け取る引数の数より少ない引数を与えて呼び出したときにエラーが出ないのも魅力的ですね。
BlockClosure で受け取る引数とはことなる数の引数を与えてもエラーが発生しないようにできないものでしょうか。

BlockClosure の受け取り引数をよしなに処理するためのメッセージ

cull: というメッセージを使うと BlockClosure が受け取る引数の数よりも多い引数を与えても平気な挙動になります。

cull も万能ではないです

この cull: を使えばブロックの引数の数と不一致な数の引数を与えて呼び出すことができるわけですが、欠点もあります。
それは何かというと、組み込みの状態からいじっていない BlockClosure では、よっつの引数までしか扱えないのです。
原因は単純によっつまでしかメソッド定義がないからです。まあ、よっつ以上の引数なら別の方法を使った方がよくないか?という気もしますので、自然な気がします。

多くの引数をブロックで受け取りたい場合はどうすればいいのか

value: メッセージを送信して、引数に OrderedCollection とかを与えればいいんじゃないですかね。

おわりに

今回の話のネタ、ほとんど梅澤さんにアドバイスしてもらったことをまとめただけだったりします。
すみません。すみません。すみません。

0 件のコメント: