算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

(ネタ)Kotlinで暗黙の引数いろいろ

Kotlinの関数リテラルはその引数が唯一つだけの場合、暗黙の引数としてitが使用できるのはみなさんご存知だと思います(暗黙の引数という言葉は正確じゃないような気がするけど気にしない)(知らない人は公式ドキュメントか今日発売のSoftwareDesignを見るといいよ)。

listOf(1, 2, 3, 4, 5)
  .filter { n -> 2 < n } // 引数を明示することも
  .map { it * 2 }        // itを使うこともできる。
  .let { println(it) }   // => [6, 8, 10]

Scalaっぽく

Scalaの関数リテラルの暗黙の引数は、複数の引数に対応しています。それぞれ_で参照することになりますが、参照するたびに第一引数、第二引数のように変化します。

// Scala
Seq(1, 2, 3).reduce(_ + _)

こういう感じの暗黙の引数がKotlinにも欲しかったので作ってみました。

// Scalaっぽい暗黙の引数_が使える(Int, Int)->Intな関数のためのクラス
class ScalaFunc2(val a: Int, val b: Int) {
    var count: Int = 0
    
    val _: Int
    get() = when(count++) {
        0 -> a
        1 -> b
        else -> throw IllegalStateException()
    }
}

そして独自のreduceを定義。

fun List<Int>.myReduce(f: ScalaFunc2.()->Int): Int =
    reduce { a, b -> ScalaFunc2(a, b).f()}

fScalaFunc2の拡張メソッドです。 そして実際に使ってみる!

listOf(1, 2, 3).myReduce{_ + _}

成功です! しかし残念なことにScalaFunc2<A, B, C>のように一般化できません。 ScalaFunc2_プロパティが参照されるたびにその型を変化させることができないからです。

Clojureっぽく

Clojureの無名関数における暗黙の引数も複数の引数に対応しています。 第一引数は%1、第二引数は%2という具合で参照できるので引数の呼び出し順序とか回数とかを気にせずに使用できます。

; Clojure
(reduce #(+ %1 %2) [1, 2, 3])

; わざわざ無名関数定義せず+だけでいいんだけどいい例が思いつかず...

これをKotlinで。

// Clojureっぽい暗黙の引数が使える(A, B)->Cな関数のためのクラス
class ClojureFunc2<A, B>(val _1: A, val _2: B)

fun <A> List<A>.myReduce(f: ClojureFunc2<A, A>.()->A): A =
    reduce { a, b -> ClojureFunc2<A, A>(a, b).f() }

今回は型を一般化できました。

listOf(1, 2, 3).myReduce{_1 + _2}

以上です。