13
カレー問題別解解説 2014 SoftUmeYa, LLC Masashi Umezawa CROSS 2014 言語CROSS Smalltalkによる

Smalltalkで文字列解析・集計

Embed Size (px)

DESCRIPTION

CROSS 2014 「言語CROSS」でのF#お題「カレーのメニュー解析&集計」の解答です

Citation preview

Page 1: Smalltalkで文字列解析・集計

カレー問題別解解説

2014 SoftUmeYa, LLC

Masashi Umezawa

CROSS 2014

言語CROSS

Smalltalkによる

Page 2: Smalltalkで文字列解析・集計

カレーのお題

長いので略。以下のような文字列をパース

1;パリパリチキン;肉類;500;0;

2;ロースカツ;肉類;300;0;なす,ゆでタマゴ

3;海の幸;魚介類;300;1;

4;やさい;野菜類;400;0;ゆでタマゴ

...

Page 3: Smalltalkで文字列解析・集計

解答の方針

関数型言語っぽく書く

ブロッククロージャやコレクション系の

プロトコルを駆使

変数の代入は極力避ける

ちょっとやり過ぎでアレな感じにする

結構まともになった...

• Smalltalk for Lispers – http://live.exept.de/doc/online/english/programming/stForLi

spers.html

クロージャ駆使はSmalltalk的スタイル

Page 4: 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] ]. ].

Page 5: Smalltalkで文字列解析・集計

A1

「辛さ」が2以上の注文を抽出し、

その「注文 ID」をすべて取得せよ

注文についてのクラスを導入せず、辞書を返している orders := getOrderHistory value: source. orders select: [:each | (each at: #HotFlavor) asInteger >= 2] thenCollect: [:each | each at: #OrderId].

Page 6: Smalltalkで文字列解析・集計

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]

Page 7: Smalltalkで文字列解析・集計

A3

「メニュー」がロースカツの注文について、

各「トッピング」の出現回数をカウント

Bag new in: [:bag | (orders select: [:each | (each at: #CurryMenu) = 'ロースカツ']) do: [:each | (each at: #Toppings) ifNotNil:[:toppings | bag addAll: (toppings findTokens: ',')]]. bag sortedElements]

Page 8: Smalltalkで文字列解析・集計

F-like

もう少し工夫してみる

括弧が多いのはいかがなものか

F#のパイプライン演算子(|>)は、やはり綺麗

Page 9: Smalltalkで文字列解析・集計

パイプラインの実装(1)

>> というメソッドをBlockClosureに定義

Objectにも定義

>> otherBlock ^ otherBlock value: self value

>> other ^ [self] >> other

Page 10: Smalltalkで文字列解析・集計

パイプラインの実装(2)

BlockClosureにselectやらcollectを定義

同様に、gather, sortedも用意

できた!!

collect ^ [:col | col collect: self]

select ^ [:col | col select: self]

Page 11: Smalltalkで文字列解析・集計

A1

「辛さ」が2以上の注文を抽出し、

その「注文 ID」をすべて取得せよ

注文についてのクラスを導入せず、辞書を返している orders := getOrderHistory value: source. orders >> [:each | (each at: #HotFlavor) asInteger >= 2] select >> [:each | each at: #OrderId] collect.

Page 12: Smalltalkで文字列解析・集計

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

Page 13: Smalltalkで文字列解析・集計

A3

「メニュー」がロースカツの注文について、

各「トッピング」の出現回数をカウント

(orders select: [:each | (each at: #CurryMenu) = 'ロースカツ']) >> [:each | (each at: #Toppings) ifNil:[#()] ifNotNil: [:toppings | (toppings findTokens: ',')]] collect >> [:each | each] gather >> #asBag >> #sortedElements