最近RubyからElixirに流れて、何でも関数で書きたい子供みたいな厄介な病気になってしまいました。
Kotlin触ってN日目程度なので何か思いつくたびに書き足していこうと思います
Recrucive
例えばこんなコード
defmodule String do
def multiply(text, count) do
multiply(text, count, "")
end
def multiply(_text, 0, temp) do
temp
end
def multiply(text, count, temp) do
multiply(text, count - 1, temp <> text)
end
end
String.multiply("Hello", 10000)
# => "HelloHelloHelloHelloHe........
をKotlinで実現してみたい。
fun main(args: Array<String>) {
println("Hello!".multiply(2))
}
fun String.multiply(i: Int) : String {
return this.multiple(i - 1, this)
}
tailrec fun String.multiply(i: Int, temp: String) : String {
if (i <= 0) return temp
this.multiply(i - 1, temp + this)
}
// => "Hello!Hello!"
ところがこれ何故か大きすぎる i
では java.lang.StackOverflowError
を吐いて死にます。
公式ドキュメントには
This allows some algorithms that would normally be written using loops to instead be written using a recursive function, but without the risk of stack overflow.
って書いてあるのにな…
Javaにデコンパイルしたりしてみてみた
import java.io.PrintStream;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
// ...
public final class Recursive_testKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull((Object)args, (String)"args");
String string = Recursive_testKt.multiple$default("Hello!", 20000, null, 2, null);
System.out.println((Object)string);
}
@NotNull
public static final String multiple(@NotNull String $receiver, int i, @NotNull String temp) {
do {
Intrinsics.checkParameterIsNotNull((Object)$receiver, (String)"$receiver");
Intrinsics.checkParameterIsNotNull((Object)temp, (String)"temp");
if (i == 0) break;
temp = temp + $receiver;
--i;
} while (true);
return temp;
}
}
while
になっている…なぜStack Overflowするのか
よく使う "string".repeat
(実装) は CharSequence
で定義してあって、その内部実装では StringBuilder
を使っていました。
と思ってこんなふうにしてみたんですが
fun main(args: Array<String>) {
println("Hello!".multiply(2000))
}
fun CharSequence.multiply(i:Int) : String {
return StringBuilder(this).multiply(i - 1, StringBuilder()).toString()
}
tailrec fun StringBuilder.multiply(i:Int, temp:StringBuilder) : StringBuilder {
return if (i <= 0) temp else this.multiply(i - 1, temp.append(this))
}
だめでした。頑張って調べよう…
Pipe Operator (Elixir や F# にあるあれ)
ないです
https://discuss.kotlinlang.org/t/pipe-forward-operator/2098/21
↑を参考にそれっぽくは書けます
fun main(args: Array<String>) {
"Hello"
.run { plus("World!") }
.run { repeat(2) }
.let { println(it) }
}
// => "HelloWorld!HelloWorld!"
続ける関数によって run
let
apply
を使い分けなければならないのが面倒ですね
2017/5/24 追記
T.apply
の実装を見てみたところ
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
となっていて、 副作用を起こしてもとのオブジェクトを返す (構造体でなければ返るものは渡したものと同じ)のでこの場合使えない気がします。つまり破壊的な作業を apply
すれば良いのですがこの場合そんなことしないと思いますので…
となると apply
は実質使わないのでは
リンク
- Kotlin ❤ FP
https://blog.plan99.net/kotlin-fp-3bf63a17d64a