今週は第29回を開催しました。
次で30回目……!!
前回の様子はコチラ↓
[第29回の様子]
2020/09/02に第29回を開催した。
今回は通常どおり水曜日の夕方に開催。ただし次回からは参加メンバーのチームミーティングの都合により午後一で開催予定。
参加メンバーは自分をいれて6名!今回も大盛況と言って過言ではない。
余談だが、最近社内で別の「dojo(道場)」と呼ばれる勉強会が始まったらしく、我々もその流儀に習ってKotlin dojoと名前を変えようかと考えている。参加者には一言も伝えてないけど。本家dojoの方は30人近い参加者がいるらしいので、半年先輩にあたるこちらの勉強会も負けていられない。
勉強会本編の内容としては、練習問題集Kotlin Koansの第3章 Collectionsをちまちまと進めた。前回未完に終わったFold
を何とか回答しおえたところで時間ぎれ。進捗は73%となり、前回比+3%。
進捗がものすごくよい、とは言えないが、後述のとおりとても良い学びがあったのでヨシ。
[学んだことや疑問点]
- Fold
- 前回ちらっと概念にふれた畳み込みの再確認から
- 集計方法として例にあがっていたのは積だけど、和でもなんでも二項演算ならオッケーということらしい。言われれば確かに。
- 設問を改めて確認すると、全ての顧客によって注文された商品を抽出せよというもの。
- 前々回の勉強会で圧倒的進捗を見せてくれたメンバーがドライバー役を務めて方針を立ててくれた
- 前提として、データ構造はShop-Customer-Order-Productという関係性になっている
- なので、注文された全ての商品の集合をつくり、それを初期値として、顧客ごとの注文商品と積集合をとっていけばよい
- ということでドライバーが仕上げたコードはおよそ次の通り
// Return the set of products that were ordered by every customer fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> { val allProducts = customers.flatMap { it.orders.flatMap { it.products } }.toSet() return customers.fold(allProducts, { acc, customer -> return acc.intersect(customer.orders.flatMap{ it.products }) }) } // 以下のエラーでテストが通らない // Fail: testGetProductsOrderedByAllCustomers: // The function 'getSetOfProductsOrderedByEveryCustomer' is implemented incorrectly expected: // <['IntelliJ IDEA Ultimate' for 199.0]> // but was:<['IntelliJ IDEA Ultimate' for 199.0, 'WebStorm' for 49.0]>
- コメントで記載したとおり、上記のコードではテストが通らなかった。最初に立ててくれた方針通りの実装のはずなのになぜ……?
- 別のメンバーが手元の環境で同じようなコードを書いて動作させたところ、テストが通ったとの報告があった。そのときのコードは以下の通り。
// Return the set of products that were ordered by every customer fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> { val allProducts = customers.flatMap { it.orders.flatMap { it.products } }.toSet() return customers.fold(allProducts, { acc, customer -> acc.intersect(customer.orders.flatMap{ it.products }) }) }
// Return the set of products that were ordered by every customer fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> { val allProducts = customers.flatMap { it.orders.flatMap { it.products } }.toSet() return customers.fold(allProducts, { acc, customer -> return@fold acc.intersect(customer.orders.flatMap{ it.products }) }) }
- 公式サイトには以下のように説明があった。無名関数とラムダ式の場合でも
return
の挙動が違うらしい。fmfm...
Higher-Order Functions and Lambdas - Kotlin Programming Language
One other difference between lambda expressions and anonymous functions is the behavior of non-local returns. A return statement without a label always returns from the function declared with the fun keyword. This means that a return inside a lambda expression will return from the enclosing function, whereas a return inside an anonymous function will return from the anonymous function itself.
- また、ラベル付き
return
の書き方は何通りかあるようだった。
Returns and Jumps: break and continue - Kotlin Programming Language
// Return the set of products that were ordered by every customer fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> { val allProducts = customers.flatMap { it.orders.flatMap { it.products } }.toSet() return customers.fold(allProducts, mylabel@{ acc, customer -> return@mylabel acc.intersect(customer.orders.flatMap{ it.products }) }) }
- または、ラムダ式ではなく無名関数を利用する
// Return the set of products that were ordered by every customer fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set<Product> { val all = customers.flatMap { it.orders.flatMap { it.products } }.toSet() return customers.fold(all, fun(acc: Set<Product>, customer: Customer): Set<Product> { return acc.intersect(customer.orders.flatMap{ it.products }) }) }
[まとめ]
今回も引き続き練習問題集Kotlin Koansを進めた。
今回はばたばたしていて「やったー!」をやり損ねてしまったが、ラムダ式のreturnに関する理解を深めることができてとても有益だった。
来週も引き続き第3章をやっていくぞ!