LoginSignup
15
8

More than 5 years have passed since last update.

KotlinでもClojureの便利な関数を使いたい

Posted at

関数型言語に特有?のリスト操作系の関数をKotlinではどうすれば使えるのか調べて&作ってみました。

リストの構築

range.clj
(def list1 (range 1 6))
(def list2 [6 6 7 8 7 7])
range.kt
val list1 = IntRange(1, 5)
val list2 = listOf(6, 6, 7, 8, 7, 7)

KotlinのほうはClosedRangeなんですね。

リストの連結

concat.clj
(conj list2 10)
(concat list1 list2)
concat.kt
list2 + 10
list1 + list2

無限リスト

repeat.clj
(->> (repeat 123) (take 3))
(->> (iterate #(* 4 %) 3) (take 3))
repeat.kt
generateSequence { 123 }.take(3)
generateSequence(3) { it * 4 }.take(3)

リストの操作(基本)

フィルタリング

filter, remove

filter.clj
(filter even? list1)
(remove even? list1)
filter.kt
list1.filter { it % 2 == 0 }
list1.filterNot { it % 2 == 0 }

some

some.clj
(some #(when (even? %) %) list1)
some.kt
list1.find { it % 2 == 0 }
list1.first { it % 2 == 0 }

条件に合致するものがなかった場合、findはnullを返し、firstは例外発生になります。

マッピング

map.clj
(map #(* % 2) list1)
(keep #(when (> % 3) (* % 2)) list1)
(map-indexed #(* %1 %2) list1)
(keep-indexed #(when (> % 3) (* % %2)) list1)
(mapcat #(list % (* % %)) list1)
map.kt
list1.map { it * 2 }
list1.mapNotNull { if (it > 3) it * 2 else null }
list1.mapIndexed { i, v -> i * v }
list1.mapIndexedNotNull { i, v -> if (i > 3) i * v else null }
list1.flatMap { listOf(it, it * it) }

畳みこみ

reduce.clj
(reduce #(* %1 %2) input)
(reduce #(* %1 %2) 10 input)
(reduce #(if (> % 20) (reduced %) (* % %2)) input)
reduce.kt
list1.reduce { acc, v -> acc * v }
list1.fold(10) { acc, v -> acc * v },
list1.reduce { acc, v -> if (acc > 20) return@reduce acc else acc * v }

reduceを途中でやめたいときはラベル付きreturnが使えます。

リストの操作(応用)

だんだん作らないといけないものが増えてきます。

重複削除、連続する重複を削除

distinct.clj
(distinct list2)
(dedupe list2)
distinct.kt
fun <T> Iterable<T>.dedupe(): List<T> {
    if (count() == 0) return toList()
    return fold(listOf(first())) { acc, v -> if (v != acc.last()) acc + v else acc }
}

list2.distinct()
list2.dedupe()

シャッフル

shuffle.clj
(shuffle list2)
shuffle.kt
import java.util.Collections

fun <T> Iterable<T>.shuffle(): List<T> = toMutableList().apply { Collections.shuffle(this) }

list2.shuffle()

applyは便利ですね。

グループ化Map、出現回数Mapの構築

groupby.clj
(group-by #(mod % 2) list2)
(frequencies list2)
groupby.kt
fun <T> Iterable<T>.frequencies(): Map<T, Int> = groupingBy { it }.eachCount()

list2.groupBy { it % 2 }
list2.frequencies()

値のはさみ込み

interleave.clj
(interleave list1 list2)
(interpose 999 list1)
interleave.kt
fun <T> Iterable<T>.interleave(list: Iterable<T>): List<T> = zip(list).flatMap { it.toList() }

fun <T> Iterable<T>.interpose(sep: T): List<T> =
        zip(Collections.nCopies(count(), sep)).flatMap { it.toList() }.dropLast(1)

list1.interleave(list2)
list1.interpose(999)

累積計算

reductions.clj
(reductions * list1)
(reductions * 10 list1)
reductions.kt
fun <T> Iterable<T>.reductions(function: (T, T) -> T): List<T> {
    if (count() <= 1) return toList()
    return drop(1).reductions(first(), function)
}

fun <T, R> Iterable<T>.reductions(init: R, function: (R, T) -> R): List<R> {
    if (count() == 0) return listOf(init)
    return fold(listOf(init)) { acc, v -> acc + function(acc.last(), v) }
}

list1.reductions { i, j -> i * j }
list1.reductions(10, Int::times)

n個おきにリストを分割

partition.clj
(partition 3 list1)
(partition-all 3 list1)
(partition 4 2 list1)
(partition-all 4 2 list1)
(partition 4 2 [:a :b] list1)
partition.kt

fun <T> Iterable<T>.partition(n: Int, step: Int = n, pad: List<T> = listOf<T>()): List<List<T>> {
    var tmp = toList()
    return mutableListOf<List<T>>().apply {
        while (tmp.count() >= n) {
            add(tmp.take(n))
            tmp = tmp.drop(step)
        }
        if (!pad.isEmpty() && !tmp.isEmpty())
            add((tmp + pad).take(n))
    }
}

fun <T> Iterable<T>.partitionAll(n: Int, step: Int = n): List<List<T>> {
    var tmp = toList()
    return mutableListOf<List<T>>().apply {
        while (!tmp.isEmpty()) {
            add(tmp.take(n))
            tmp = tmp.drop(step)
        }
    }
}

list1.partition(3)
list1.partitionAll(3)
list1.partition(4,2)
list1.partitionAll(4,2)
list1.partition(4,2,listOf("a","b"))

Kotlinにもともとあるpartition関数はPair<List<T>, List<T>>を返すので用途が違います。

条件によってリストを分割

partitionby.clj
(partition-by identity list2)
(partition-by #(> % 7) list2)
partitionby.kt
fun <T, R> Iterable<T>.partitionBy(function: (T) -> R): List<List<T>> {
    if (count() == 0) return listOf()

    return drop(1).fold(listOf(listOf(first()))) { acc, v ->
        if (function(acc.last().last()) != function(v))
            acc + listOf(listOf(v))
        else
            acc.dropLast(1) + listOf(acc.last() + v)
    }
}

list2.partitionBy { it }
list2.partitionBy { it > 7 }
15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8