Download - Smalltalkで文字列解析・集計
![Page 1: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/1.jpg)
カレー問題別解解説
2014 SoftUmeYa, LLC
Masashi Umezawa
CROSS 2014
言語CROSS
Smalltalkによる
![Page 2: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/2.jpg)
カレーのお題
長いので略。以下のような文字列をパース
1;パリパリチキン;肉類;500;0;
2;ロースカツ;肉類;300;0;なす,ゆでタマゴ
3;海の幸;魚介類;300;1;
4;やさい;野菜類;400;0;ゆでタマゴ
...
![Page 3: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/3.jpg)
解答の方針
関数型言語っぽく書く
ブロッククロージャやコレクション系の
プロトコルを駆使
変数の代入は極力避ける
ちょっとやり過ぎでアレな感じにする
結構まともになった...
• Smalltalk for Lispers – http://live.exept.de/doc/online/english/programming/stForLi
spers.html
クロージャ駆使はSmalltalk的スタイル
![Page 4: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/4.jpg)
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で文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/5.jpg)
A1
「辛さ」が2以上の注文を抽出し、
その「注文 ID」をすべて取得せよ
注文についてのクラスを導入せず、辞書を返している orders := getOrderHistory value: source. orders select: [:each | (each at: #HotFlavor) asInteger >= 2] thenCollect: [:each | each at: #OrderId].
![Page 6: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/6.jpg)
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で文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/7.jpg)
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で文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/8.jpg)
F-like
もう少し工夫してみる
括弧が多いのはいかがなものか
F#のパイプライン演算子(|>)は、やはり綺麗
![Page 9: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/9.jpg)
パイプラインの実装(1)
>> というメソッドをBlockClosureに定義
Objectにも定義
>> otherBlock ^ otherBlock value: self value
>> other ^ [self] >> other
![Page 10: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/10.jpg)
パイプラインの実装(2)
BlockClosureにselectやらcollectを定義
同様に、gather, sortedも用意
できた!!
collect ^ [:col | col collect: self]
select ^ [:col | col select: self]
![Page 11: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/11.jpg)
A1
「辛さ」が2以上の注文を抽出し、
その「注文 ID」をすべて取得せよ
注文についてのクラスを導入せず、辞書を返している orders := getOrderHistory value: source. orders >> [:each | (each at: #HotFlavor) asInteger >= 2] select >> [:each | each at: #OrderId] collect.
![Page 12: Smalltalkで文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/12.jpg)
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で文字列解析・集計](https://reader034.vdocuments.mx/reader034/viewer/2022051818/54b6e05d4a79599a1a8b4635/html5/thumbnails/13.jpg)
A3
「メニュー」がロースカツの注文について、
各「トッピング」の出現回数をカウント
(orders select: [:each | (each at: #CurryMenu) = 'ロースカツ']) >> [:each | (each at: #Toppings) ifNil:[#()] ifNotNil: [:toppings | (toppings findTokens: ',')]] collect >> [:each | each] gather >> #asBag >> #sortedElements