Re: Method Finder for ruby

id:shinichiro_h さんが、Squeak の Method Finder にヒントを得て、さっそく Ruby で使える Method Finder をこしらえて公開してくださいました。動的言語 and/or 動的システム、万歳!ですね。w


毎度お馴染みの直訳ぎみに。

| receiver args expected matches tt return filled |

filled := FillInTheBlank request: '{receiver. argsArray. expected}' initialAnswer: '#(3 (4) 7)'.
filled := [Compiler evaluate: filled] ifError: [nil].
([filled size] ifError: [0]) ~= 3 ifTrue: [^ self].

receiver := filled first.
args := filled second.
expected := filled third.

matches := OrderedCollection new.

receiver class selectors do: [: selector |
   [  tt := [receiver clone] ifError: [receiver].
      return := tt perform: selector withArguments: args.
      return = expected ifTrue: [
         matches add: receiver class printString, ' >> ', selector printString]] ifError: []].
^ matches
an OrderedCollection('SmallInteger >> #bitOr:' 'SmallInteger >> #+' 'SmallInteger >> #bitXor:')

注意:
こののスクリプトは動くことは動きますが、3 + 4 以外の例を試すときは、あらかじめ仮想イメージ(とチェンジセットの組)のバックアップをとってから行なうことを強くお薦めします。



じつは、Ruby の Object#methods は、SqueakSmalltalk では Behavior >> #allSelectors に相当します。が、これをやるととんでもないことになる(^_^;)ので、ここではあえて Behavior >> #selectors(それが属するクラスに定義されているメソッド名一覧)どまりにしてあります(じっさいの MethodFinder ではあらかじめテーブルを作って、それこそ最強に凶悪な #become: とかいったヤヴァめのメソッドはチェックから排除しています)。



余談ですが、Smalltalk のメソッド名(セレクタ)は、引数個数情報も「:」の数(あるいは、セレクタのタイプ)として含んでいるので、事前に引数の数も一致するものだけにしぼってから #do: (#select:thenDo:)することも可能でしょう。ここいらへんは、変態文法ならでは、かと。

receiver class selectors
   select: [: selector | selector numArgs = args size]
   thenDo: [: selector |
      [  tt := [receiver clone] ifError: [receiver].
         return := tt perform: selector withArguments: args.
         return = expected ifTrue: [
            matchies add: receiver class printString, ' >> ', selector printString]] ifError: []].


Ruby では、メソッド名からメソッドをオブジェクト化する手順を踏めば、そこで引数の数の情報を Method#arity で得ることができます。が、この例ではちょっとコスト高で本末転倒になってしまいかねませんね。 あと、Ruby のメソッドは引数可変のものもあるので、情報としてはあまり役に立たなそう…でもあります(^_^;)。

3.method(:+).arity  #=> 1


もちろん Smalltalk でも、(セレクタだけでなく)メソッド本人も引数の数を知っていて、セレクタと同様に numArgs メッセージを送れば答えてくれます。

(SmallInteger >> #+) numArgs  " => 1 "


Squeak の MethodFinder を GUI を介さずに使用するときは、次のように書きます。(なお、Method Finder で GUI を担当するのは、タイトルこそ Method Finder と書いてありますが実体は a SelectorBrowser という別のオブジェクトです。←さっきソースを見て知った(^_^;))

MethodFinder methodFor: #((3 4) 7)
=>  '(data1 bitOr: data2) (data1 bitXor: data2) (data1 + data2) '
MethodFinder methodFor: {{'29 Apr 1999' asDate}. 'Thursday'}
=> '(data1 weekday) '


こんなふうにして、例を増やしてリファインすることも可能です。この機能も Ruby 版に取り込まれるとよいかもしれませんね。

MethodFinder methodFor: #((3 4) 7 (0 5) 5 (5 5) 10).
=>  '(data1 + data2) '