Ruby1.8.7 の新機能を Squeak Smalltalk にマップしてみる
Ruby 1.8.7で使えるようになったRuby 1.9のメソッドたち - http://rubikitch.com/に移転しました によれば、Ruby1.8.7 には、これまで Ruby1.9 でしか使えなかった機能がかなり豊富に盛り込まれたようです。そこで、列挙されているもののうち、Squeak Smalltalk(Squeak3.9)にて相当、あるいは関連ありそうな機能をピックアップして対応させてみました。
▼ ブロックにブロックを渡す
もとより Smalltalk のブロックは Ruby のブロックと異なり、引数として特別扱いされません。したがって、レシーバがブロックでも通常どおり普通にブロックを渡せます(Smalltalk のブロックは第一級オブジェクト)し、逆に Ruby と違い常にそうする必要があります(“ブロック付きメソッド呼び出し”のような特殊な構文はない)。
[:block | block value] value: ['block is passed'] "=> 'block is passed' "
▼ Object#tap
Ruby の Object#tap は Squeak Smalltalk では Object>>#in: に相当します。ただ、Object#tap は無条件でレシーバを返すのに対し、#in: はブロックの返値をそのまま返すので、うれしさは半分以下ですね。
Dictionary new in: [:dict | dict at: #one put: 1; at: #two put: 2]; yourself "=> a Dictionary(#one->1 #two->2 ) "
▼ Binding#eval
Squeak Smalltalk で実行コンテキストを指定して式を評価するのには #evaluate:in:to: が使えます。コンテキストをレシーバにして云々…という機能は見あたりませんでした。
| context | context := [| var | var := #inBlock] value; yourself. ^Compiler new evaluate: 'var' in: context to: self "=> #inBlock "
▼ __method__
実行中のメソッド名は、実行中のコンテキストを得る thisContext という擬似変数から selector でたぐれます(Smalltalk ではメソッド名をセレクタと呼びます)。
Object >> foo ^thisContext selector
Object new foo "=> #foo "
▼ Method#name
CompiledMethod>>#selector が相当します。
(Collection compiledMethodAt: #size) selector "=> #size "
▼ Method#owner
#methodClass が相当します。
(Collection compiledMethodAt: #size) methodClass "=> Collection "
▼ 平滑化レベル指定付き Array#flatten
残念ながら Squeak Smalltalk の #concatenation は、第一層固定のようです。
#((1 2 3) (4 5) (6)) concatenation "=> #(1 2 3 4 5 6) "
▼Array#shuffle
#shuffled が相当します。破壊的操作版はありません。
#(1 2 3 4 5) shuffled "=> #(3 4 5 1 2) "
▼ Array#choice
#atRandom が相当します。
#(1 2 3 4 5) atRandom "=> 5 "
▼ Array#permutation
#permutationsDo: が相当します。
#(1 2 3) permutationsDo: [:perm | Transcript cr; show: perm]
▼ Array#combination
#combinations:atATimeDo: が相当します。
#(1 2 3 4) combinations: 3 atATimeDo: [:comb | Transcript cr; show: comb]
▼ Array#pop、Array#shift
OrderedCollection>>#removeFirst: 、#removeLast: が相当します。
| colln | colln := #(1 2 3 4) asOrderedCollection. colln removeFirst: 2. ^colln "=> an OrderedCollection(3 4) "
| colln | colln := #(1 2 3 4) asOrderedCollection. colln removeLast: 2. ^colln "=> an OrderedCollection(1 2) "
▼ブロック付き Array#index、Array#rindex
#findFirst: 、#findLast: が相当します。
#(0 1 0 1 0) findFirst [:each | each > 0] "=> 2 "
#(0 1 0 1 0) findLast: [:each | each > 0] "=> 4 "
▼ Array#take
#first: が相当します。
#(1 2 3 4) first: 2 "=> #(1 2) "
▼ Enumerable#none?
#noneSatisfy: が相当します。
#(ant bear cat) noneSatisfy: [:each | each size = 5] "=> true "
▼ Enumerable#min_by、Enumerable#max_by
#detectMin: 、#detectMax: が相当します。
#(18 15 22 53) detectMin: [:each | each \\ 10] "=> 22 "
#(18 15 22 53) detectMax: [:each | each \\ 10] "=> 18 "
▼ Enumerable#find_index
#findFirst: が相当します。
(1 to: 100) findFirst: [:each | (each isDivisibleBy: 5) and: [each isDivisibleBy: 7]] "=> 35 "
▼ Enumerable#inject で二項メソッド名を指定可能に
相当する機能はありません。ただ、合計には #sum が使えます。
(1..4).inject(0){ |s,e| s+e } #=> 10
(1..4).inject(:+) #=> 10
(1 to: 4) inject: 0 into: [:sum :each | sum + each] "=> 10 "
(1 to: 4) sum "=> 10 "
なお、Smalltalk でも、Symbol>>#asBlock もしくは、Symbol>>#value:value: を定義することで、Ruby の Symbol#to_proc モドキのことは実現可能です。念のため。
Symbol >> asBlock ^[:rcvr :arg | rcvr perform: self with: arg]
(1 to: 4) inject: 0 into: #+ asBlock "=> 10 "
Symbol >> value: rcvr value: arg
^rcvr perform: self with: arg
(1 to: 4) inject: 0 into: #+ "=> 10 "
▼ Enumerable#count
#occurrencesOf:(要素指定の場合)、#count:(ブロック指定の場合)に相当します。
#(1 2 4 2) occurrencesOf: 2 "=> 2 "
#(1 2 4 2) count: [:each | each isDivisibleBy: 2] "=> 3 "
▼ Enumerable#group_by
#groupBy:having: が相当します。
(1 to: 6) groupBy: [:each | each \\ 3] having: [:group | true] "=> a PluggableDictionary( 0->an OrderedCollection(3 6) 1->an OrderedCollection(1 4) 2->an OrderedCollection(2 5) ) "
▼ Enumerable::Enumerator#next
Ruby の Enumerator は、Smalltalk の ReadStream とよく似た挙動を呈します。したがって、#next が相当すると考えてよさそうです。
| stream | stream := (1 to: 6) readStream. ^{stream next. stream next} "=> #(1 2) "
▼ Enumerable::Enumerator#rewind
同様に、#reset が相当します。
| stream | stream := (1 to: 6) readStream. stream next; next. stream reset. ^{stream next. stream next} "=> #(1 2) "
▼ String#lines、String#bytes
Squeak Smalltalk では、どちらかというと、これまでの Ruby1.8 寄りで #linesDo:、(必要なら asByteArray してから)#do: を使います。
| stream | stream := #() writeStream. 'abc\def\' withCRs linesDo: [:each | stream nextPut: each]. ^stream contents "=> #('abc' 'def') "
| stream | stream := #() writeStream. 'abc' asByteArray do: [:each | stream nextPut: each]. ^stream contents "=> #(97 98 99) "
▼ String#chars、String#each_char
もとより Squeak Smalltalk では #do:(Ruby の #each に相当)は文字単位です。
'おはよう' as: Array "=> #($お $は $よ $う) "
OrderedCollection new in: [:colln | 'おはよう' do: [:each | colln add: each]]; yourself "=> an OrderedCollection($お $は $よ $う) "
▼ String#start_with?、String#end_with?
#beginsWith: 、#endsWith: が相当します。
'あいうえお' beginsWith: 'あい' "=> true "
'あいうえお' endsWith: 'お' "=> true "
▼ Integer#odd?、Integer#even?
#odd、#even が相当します。
1 odd "=> true "
1 even "=> false "
12 odd "=> false "
12 even "=> true "