はじめに
公式の問題集「Kotlin Koans」を解きながらKotlinを学習します。
過去記事はこちら
- Introduction
- Classes
- Conventions
- Collections
問題
foldとreduceについて学び、fold
を使ってすべての顧客が注文したことのある商品の集合を返す関数を実装します。
前のタスクで定義した Customer.getOrderedProducts() を使用することができます(その実装をコピーしてください)。
修正前コード.kt
// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
TODO()
}
fun Customer.getOrderedProducts(): List<Product> =
TODO()
Shop.kt
data class Shop(val name: String, val customers: List<Customer>)
data class Customer(val name: String, val city: City, val orders: List<Order>) {
override fun toString() = "$name from ${city.name}"
}
data class Order(val products: List<Product>, val isDelivered: Boolean)
data class Product(val name: String, val price: Double) {
override fun toString() = "'$name' for $price"
}
data class City(val name: String) {
override fun toString() = name
}
問題のポイント
reduce()、fold()関数
foldの使用例
listOf(1, 2, 3, 4)
.fold(1) { partProduct, element ->
element * partProduct // (1)
} == 24
reduce()、fold()関数は与えられた操作をコレクション要素に順次適用し、蓄積された結果を返します。
前に蓄積された値とコレクション要素を2つの引数にとります。
2つの関数の違いは、fold()が初期値を受け取り、それを第1ステップで累積値として使用するのに対し、reduce()は第1ステップで第1要素と第2要素を操作引数として使用する点です。
val numbers = listOf(5, 2, 10, 4)
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled) // 42 (0+5*2)->(10+2*2)->(14+10*2)->(34+4*2)
//最初の要素が結果で2倍になっていない
val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 }
println(sumDoubledReduce) // 37 (5+2*2)->(9+10*2)->(29+4*2)
上の例では、fold()は2倍された要素の合計を計算します。
同じ関数を reduce() に渡すと、別の結果を返します。
なぜなら、最初のステップでリストの 1 番目と 2 番目の要素を引数として使用するので、最初の要素は 2 倍にならないからです。
intersect()関数
intersectを用いることで、両方のセットに含まれている要素からなる集合を求めることができます。
val numbers1 = setOf(1, 2, 3)
val numbers2 = setOf(2, 3, 4)
println(numbers1.intersect(numbers2)) // [2, 3]
解答例
// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
// allOrderedProductsは顧客から注文されたすべての商品の集合
val allOrderedProducts = customers.flatMap { it.getOrderedProducts() }.toSet()
return customers.fold(allOrderedProducts) { allOrdered, customer ->
allOrdered.intersect(customer.getOrderedProducts().toSet())
}
}
fun Customer.getOrderedProducts(): List<Product> =
orders.flatMap(Order::products)