狠狠撸

狠狠撸Share a Scribd company logo
CROSS 2014
言語CROSS

Smalltalkによる

カレー問題別解解説

2014 SoftUmeYa, LLC
Masashi Umezawa
カレーのお題
長いので略。以下のような文字列をパース
1;パリパリチキン;肉類;500;0;
2;ロースカツ;肉類;300;0;なす,ゆでタマゴ
3;海の幸;魚介類;300;1;
4;やさい;野菜類;400;0;ゆでタマゴ
...
解答の方針
?関数型言語っぽく書く
ブロッククロージャやコレクション系の
プロトコルを駆使
変数の代入は極力避ける
ちょっとやり過ぎでアレな感じにする
結構まともになった...
? Smalltalk for Lispers
– http://live.exept.de/doc/online/english/programming/stForLi
spers.html

クロージャ駆使はSmalltalk的スタイル
getOrderHistory
? 「関数を定義」とあるので、全体をクロージャに
注文クラスを導入せず、辞書を返している
getOrderHistory := [:src | | rows rowStream |
rows := OrderedCollection new.
rowStream := src readStream.
[rowStream atEnd]
whileFalse: [rows add: ((rowStream nextLine) findTokens: ';')].
rows collect: [:eachRow |
Dictionary new in: [:map |
#(#OrderId #CurryMenu #Category
#RiceWeight #HotFlavor #Toppings)
paddedWith: eachRow do: [:a :b | map add: (a->b)].
map]
].
].
A1
? 「辛さ」が2以上の注文を抽出し、
その「注文 ID」をすべて取得せよ
注文についてのクラスを導入せず、辞書を返している
orders := getOrderHistory value: source.

orders
select: [:each | (each at: #HotFlavor) asInteger >= 2]
thenCollect: [:each | each at: #OrderId].
A2
? 「分類」ごとに「ライスの量」の平均を取得せよ。
なお、平均値が大きい順に並べる
((orders groupBy: [:each | (each at: #Category)]
having: [:each | true])
collect: [:each |
(each collect: [:e | e at: #RiceWeight]) average])
associations sorted: [:a :b | a value > b value]
A3
? 「メニュー」がロースカツの注文について、
各「トッピング」の出現回数をカウント
Bag new in: [:bag |
(orders select:
[:each | (each at: #CurryMenu) = 'ロースカツ'])
do: [:each | (each at: #Toppings)
ifNotNil:[:toppings | bag addAll:
(toppings findTokens: ',')]].
bag sortedElements]
F-like
?もう少し工夫してみる
括弧が多いのはいかがなものか
F#のパイプライン演算子(|>)は、やはり綺麗
パイプラインの実装(1)
? >> というメソッドをBlockClosureに定義
>> otherBlock
^ otherBlock value: self value
? Objectにも定義
>> other
^ [self] >> other
パイプラインの実装(2)
? BlockClosureにselectやらcollectを定義

collect
^ [:col | col collect: self]
select
^ [:col | col select: self]
同様に、gather, sortedも用意

? できた!!
A1
? 「辛さ」が2以上の注文を抽出し、
その「注文 ID」をすべて取得せよ
注文についてのクラスを導入せず、辞書を返している
orders := getOrderHistory value: source.

orders
>> [:each | (each at: #HotFlavor) asInteger >= 2] select
>> [:each | each at: #OrderId] collect.
A2
? 「分類」ごとに「ライスの量」の平均を取得せよ。
なお、平均値が大きい順に並べる
(orders groupBy: [:each | (each at: #Category)]
having: [:each | true])
>> [:each |
(each collect: [:e | e at: #RiceWeight]) average]) collect
>> #associations
>> [:a :b | a value > b value] sorted
A3
? 「メニュー」がロースカツの注文について、
各「トッピング」の出現回数をカウント
(orders
select: [:each | (each at: #CurryMenu) = 'ロースカツ'])
>> [:each | (each at: #Toppings) ifNil:[#()] ifNotNil:
[:toppings | (toppings findTokens: ',')]] collect
>> [:each | each] gather
>> #asBag
>> #sortedElements

More Related Content

厂尘补濒濒迟补濒办で文字列解析?集计