LoginSignup
3
4

More than 5 years have passed since last update.

Kotlinで関数型プログラミング風

Last updated at Posted at 2017-05-19

最近RubyからElixirに流れて、何でも関数で書きたい子供みたいな厄介な病気になってしまいました。
Kotlin触ってN日目程度なので何か思いつくたびに書き足していこうと思います

Recrucive

例えばこんなコード

Elixir
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で実現してみたい。

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にデコンパイルしたりしてみてみた

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 を使っていました。

と思ってこんなふうにしてみたんですが

Kotlin
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実装を見てみたところ

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 は実質使わないのでは

リンク

3
4
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
3
4