【问题标题】:Functional way to accumulate within a Unit function in Kotlin?在 Kotlin 的 Unit 函数中累积的函数方式?
【发布时间】:2017-11-07 12:32:10
【问题描述】:

我试图强迫自己在 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)
}

【问题讨论】:

    标签: functional-programming kotlin


    【解决方案1】:

    仅使用 kotlin-stdlib 并保留代码语义(即仅迭代一次)的一种方法是使用 convert the Iterable&lt;T&gt; to Sequence&lt;T&gt; 并使用 .drop(n) 扩展名:

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

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

    当您有一个自定义的高阶函数迭代项目并且只接受回调但不返回任何内容时,您可以将该自定义迭代逻辑包装到 Sequence&lt;T&gt; 中,方法是使用 buildSequence { ... } 并传递 yield(it)作为回调:

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

    这允许您以函数式样式使用此序列,特别是折叠序列:

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

    【讨论】:

    • 谢谢...我可能应该想出一个更好的 forEachFrom 示例。 :) 我不是试图改进该功能,而是更多地试图了解是否可以以纯粹的功能方式在返回单元的函数中累积测试字符串——而不在封闭范围内使用var?我有一堆更复杂的Unit-returning 函数,我需要为其编写测试。这更像是一种练习,让我思考是否没有比var s 更好的选择。
    • @sirksel,基本上,在函数式风格中累积状态通常使用 fold 习语(在 Kotlin 中有一个 fold extension function)。
    • @sirksel,这是上面函数的示例实现,fold(gist)
    • 您可以根据需要使累加器类型变得复杂,例如使用 data 类,为您需要累积的每个值提供一个属性。
    • 我想不通的是如何将fold 与返回单元的函数一起使用。换句话说,假设我无法更改forEachFrom。如果函数返回一个值,我会知道如何应用折叠或减少或不通过链接。有没有办法在 act 块中使用 lambda(与修改被测试的基本函数完全分开),它采用折叠概念但不引用封闭范围内的 var?如果有,我就是无法正确地使用语法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-13
    • 2020-01-26
    • 1970-01-01
    • 2012-07-18
    • 2012-05-31
    • 2021-10-14
    • 2022-01-15
    相关资源
    最近更新 更多