在Kotlin中积累单元函数的功能方法?

时间:2017-11-07 12:32:10

标签: functional-programming kotlin

我正试图强迫自己在Kotlin中使用函数式编程,并尽可能避免使用可变的变量。通常,对于单元返回函数的 ad hoc 测试,我只需在函数内部println()查看它是否正常工作。但是对于这个测试,我需要累积一个字符串,然后最终使用assertEquals(...)

像往常一样,我发现自己在封闭范围内宣布var并使用+=积累到其中。 通过传递/链接函数并消除可变变量来实现这一功能是否有更多功能?以下是一些简化但具有说明性的代码:

inline fun <T> Iterable<T>.forEachFrom(beg:Int, act:(T)->Unit) {
  var i=0;  if (beg>=0) for (e in this) if (i++ >= beg) act(e)
}

fun main(args:Array<String>) {
  val l = listOf("zero", "one", "two", "three", "four")

  // print-to-screen test
  l.forEachFrom(2){print("$it-")};  println()
  // output:  two-three-four-

  // accumulate-in-var test
  var s = ""
  l.forEachFrom(2){s += "$it-"};  println(s)
  // output:  two-three-four-

  // Is there a purely functional way, without declaring a mutable var?
  // val s = l.forEachFrom(2){accumulator???("$it-")}
  // - OR -
  // val s = l.forEachFrom(2).accumulator???("$it-")
  // println(s)
}

1 个答案:

答案 0 :(得分:3)

只使用kotlin-stdlib并保留代码的语义(即只迭代一次)的方法是convert the Iterable<T> to Sequence<T>并使用.drop(n)扩展名:

inline fun <T> Iterable<T>.forEachFrom(beg: Int, act: (T) -> Unit) =
    if (beg >= 0)
        asSequence().drop(beg).forEach(act) else
        Unit

UPD:在讨论了整体问题后,我们提出了另一种方法。

如果您有一个自定义高阶函数,它迭代项目并且只接受回调但不返回任何内容,您可以使用buildSequence { ... }将该自定义迭代逻辑包装到Sequence<T>中传递yield(it)作为回调:

val sequenceFromCustomFunction = buildSequence {
    l.forEachFrom(2) { yield(it) }
}

这允许您以功能样式处理此序列,特别是折叠序列:

val s = sequenceFromCustomFunction.fold("") { acc, it -> acc + it + "-" }