jsoizo
April 07, 2023
53

# よくつかっているIterableの自作extensionを紹介します

April 07, 2023

## Transcript

1
2. ### whoami • ໊લ: Jun Sekine @jsoizo • Blog: https://jsoizo.hatenablog.com/ •

ॴଐ: גࣜձࣾίυϞϯ • Server Side Kotlin͸͡Ίͯ3ϲ݄΄ͲͰ͢ • લ͸ScalaΛ͔͍ͭͬͯ·ͨ͠, akka͕޷͖Ͱͨ͠ • KotlinͰ͸Arrow.kt͕޷͖ • Ϟφυͱ͔͸Α͘Θ͔ΒΜͰ͢
3. ### Intro ScalaൺͰKotlin͸ग़དྷΔ͜ͱ͕͍͍ҙຯͰݶఆతͰஸ౓͍͍ͱײ͡·͢ɻ ҰํͰ഑ྻૢ࡞ʹ͓͍ͯScalaͩͱޮ཰ྑ͘=૸ࠪճ਺͕গͳ͘Ͱ͖Δ͜ͱ͕ɺ KotlinͩͱͰ͖ͣਏΈΛײͨ͜͡ͱ͕͋ΓextensionΛ࡞ͬͨͷͰ঺հ͠·͢ɻ • partitionMap<F, S> = partition &

map, partitionMap<F, S>(Either൛) • collect<B> = fi lter & map, collectFirst<B> = fi nd & map • groupMapReduce<K, V> = groupBy & reduce
4. ### partitionMap<F, S> = partition & map partition͸഑ྻͷ֤ཁૉʹରͯ͠predicateΛ࣮ߦ͠ ݁Ռ͕trueͳΒPairͷ fi rst,

falseͳΒsecondʹͳΔ͕ɺ patitionMap͸ fi rst, secondͷ஋ΛͦΕͧΕmapͨ݁͠ՌΛฦ٫͢Δɻ fun <A, F, S> Iterable<A>.partitionMap( predicate: (A) -> Boolean, transformFirst: (A) -> F, transformSecond: (A) -> S ): Pair<List<F>, List<S>> = this.fold(Pair(emptyList(), emptyList())) { acc, it -> val (fList, sList) = acc if (predicate(it)) Pair(fList, sList.plus(transformSecond(it))) else Pair(fList.plus(transformFirst(it)), sList) }
5. ### partitionMap<F, S> (Either൛) transformͷ݁ՌΛArrow.ktͷEitherͱ͢Δ͜ͱͰɺ ScalaͷpartitionMapͱಉ͡γάωνϟͱ͍ͯ͠Δɻ ม׵݁ՌͷEither͕Leftͷ৔߹͸ fi rst, Rightͷ৔߹͸secondʹ٧ΊΔɻ EitherͰόϦσʔγϣϯΛ࣮૷͍ͯ͠ΔࡍͷҰׅॲཧͳͲͰศར

※ ͓ͦΒ͘kotlin-resultͷResultΛར༻ͯ͠΋ಉ͜͡ͱΛ࣮ݱͰ͖Δ͸ͣ fun <A, F, S> Iterable<A>.partitionMap( transform: (A) -> Either<F, S> ): Pair<List<F>, List<S>> = this.fold(Pair(emptyList(), emptyList())) { acc, it -> val (fList, sList) = acc transform(it).fold({ Pair(fList.plus(it), sList) }, { Pair(fList, sList.plus(it)) })
6. ### partitionMap<F, S> (Either൛) csvಡΈࠐΈ౳ʹΑΓෳ਺ͷΦϒδΣΫτΛҰׅͰੜ੒͠ɺ ͢΂ͯόϦσʔγϣϯ͕௨ͬͨ৔߹ͷΈอଘ͞ΕΔɺͱ͍͏ྫ data class PersonValidateError: DomainError data

class Person private constructor(val name: String, val age: Int) { companion object { fun of(name: String, age: Int): Either<PersonValidateError, Person> } } data class Row(val col1: String, val col2: Int) val rows = listOf(Row("Alice", 20), Row("Bob", -2), Row("", 35)) val (errors, validatedPersons) = rows.partitionMap({ Person.of(it.col1, it.col2) }) // errors: List<PersonValidateError> validatedPersons: List<Person> if (errors.isNotEmpty()) savePersons(validatedPersons)
7. ### partitionMap<F, S> (Either൛ ͜΅Ε࿩) Arrow.ktʹPull Requestग़ͯ͠࠷ऴతʹ͜͏ͳͬͨ👏1.2.0ʹೖΓ·͢🎉 https://github.com/arrow-kt/arrow/pull/3004 fun <A, F,

S> Iterable<A>.separateEither( transform: (A) -> Either<F, S> ): Pair<List<F>, List<S>> { val left = mutableListOf<F>() val right = mutableListOf<S>() for (item in this) { when (val either = transform(item)) { is Either.Left -> left.add(either.value) is Either.Right -> right.add(either.value) } } return Pair(left, right) }
8. ### collect<B> = fi lter & map<B> ഑ྻͷ֤ཁૉʹରͯ͠conditionΛ࣮ߦ͠ɺ ݁Ռ͕trueͷ৔߹͸ͦͷཁૉΛtransformʹ͔͚ͨ݁ՌΛฦ٫͢Δɻ ※ Scalaͷcollect͸Ҿ਺͕PartialFunction[A,

B]ͳͷͰ΋ͬͱෳࡶͳ৔߹෼͚͕Մ (PartialFunction ≒ ࢦఆͨ͠৚݅ԼͰͷΈ஋Λฦ͢ύλʔϯϚον) Ώ͑ʹยखམͪײ͕൱Ίͣɺਖ਼௚mapNotNullͰे෼ͱײ͡Δ͜ͱ͕ଟ͍ fun <A, B> Iterable<A>.collect(condition: (A) -> Boolean, transform: (A) -> B): List<B> = this.mapNotNull { if (condition(it)) transform(it) else null }
9. ### collect<B> = fi lter & map<B> ςετͷ఺਺Ϧετͷத͔Βӳޠͷ఺਺͚ͩΛநग़͠੔ܗ͢Δྫ val scores =

listOf( Triple("Alice", "Math", 90), Triple("Alice", "English", 78), Triple("Bob", "Math", 50), Triple("Bob", "English", 80), ) val englishScoresPrettyPrinted = scores.collect( { it.second == "English" }, // condition: (T) -> Boolean { "\${it.first} - \${it.third}఺" } // transform: (T) -> A ) // Alice - 78఺ // Bob - 80఺
10. ### collectFirstOrNull<B> = collect<B> & fi rstOrNull<B> ഑ྻͷ֤ཁૉʹରͯ͠conditionΛ࣮ߦ͠ɺ ࠷ॳʹ݁Ռ͕trueʹͳͬͨཁૉΛtransformʹ͔͚ͯฦ٫͢Δɻ condition͕trueʹͳΔཁૉ͕ͳ͍৔߹͸nullΛฦ٫͢Δɻ fi

nd{ condition(it) }?.let { transform(it) } ͱಉ༷ͱ΋͍͑Δɻ ͜Ε·ͨಉ༷ʹPartialFunction͕ͳ͍Ώ͑ fi rstNotNullOfOrNullͰࣄ଍ΓΔ͜ͱଟ͠… fun <A, B> Iterable<A>.collectFirstOrNull( condition: (A) -> Boolean, transform: (A) -> B ): B? = this.firstNotNullOfOrNull { if (condition(it)) transform(it) else null }
11. ### collectFirstOrNull<B> = collect<B> & fi rstOrNull<B> ςετͷ఺਺Ϧετͷத͔Βӳޠͷ఺਺͚ͩΛ1͚ͭͩநग़͠੔ܗ͢Δྫ val scores =

listOf( Triple("Alice", "Math", 90), Triple("Alice", "English", 78), Triple("Bob", "Math", 50), Triple("Bob", "English", 80), ) val emptyScores = emptyList<Triple<String, String, Int>> val englishScoresPrettyPrinted = scores.collectFirst( { it.second == "English" }, // condition: (T) -> Boolean { "\${it.first} - \${it.third}఺" } // transform: (T) -> A ) // Alice - 78఺ // ಉ͡ॲཧΛemptyScoresʹରͯ͠ߦ͏ͱnull͕ฦ٫͞ΕΔ
12. ### groupMapReduce<K, V> groupBy<K, V>(keyTransform: (A) -> K, valueTransform: (A) ->

V) ͷvalueΛ͞Βʹreduceͨ͠΋ͷɻ σʔλͷूܭͳͲͰ༻͍Δ͜ͱ͕ଟ͍ɻ(ఆٛͯ͋͠Δ͚ͩͰࠓͷͱ͜Ζະ࢖༻) fun <A, K, V> Iterable<A>.groupMapReduce( keyTransform: (A) -> K, valueTransform: (A) -> V, valueReduce: (V, V) -> V ): Map<K, V> = this.fold(mutableMapOf()) { acc, it -> val key = keyTransform(it) val current = acc.get(key) val value = valueTransform(it) acc[key] = current?.let { valueReduce(it, value) } ?: value acc }
13. ### groupMapReduce<K, V> class ScoresRecord (val name: String, val kamoku: String,

val season: String, val value: Int) val scores = listOf( ScoresRecord("Alice", "਺ֶ", "21೥্ظ", 100), ScoresRecord("Alice", "ӳޠ", "21೥্ظ", 100), ScoresRecord("Bob", "਺ֶ", "21೥্ظ", 100), ScoresRecord("Bob", "ӳޠ", "21೥্ظ", 100), … ) value class PositiveInt(val value: Int) { fun plus(other: PositiveInt) = PositiveInt(this.value + other.value) } val scoresPerNameKamoku = scores.groupMapReduce( { Pair(it.name, it.kamoku) }, // keyTransform: (T) -> K { PositiveInt(it.value) }, // valueTransform: (T) -> V { a1, a2 -> a1.plus(a2) } // valueReduce: (V, V) -> (V) )

Arrow.kt͸͍͍ͧ