【问题标题】:reducing list of functions by calling them通过调用它们来减少函数列表
【发布时间】:2021-07-12 23:25:25
【问题描述】:

我正在使用 kotlin 中的高阶函数。

我想要一个函数列表,然后通过调用列表中的所有函数并对它们求和,将列表简化为一个结果。 像这样的:

val num =  arrayOf(1, 2, 3, 4)

val funcs = num.map <Int, (Int) -> Int > { x  -> {
        y -> y + x
}}

val res = funcs.reduce{acc, elem -> elem(1) + acc}

但是这里的底线没有编译,并声称我在归约中的 lambda 返回 Unit,这对我来说没有意义。 我也试过这样调用reduce,同样的问题仍然存在:

val res = funcs.reduce{acc, elem -> elem.invoke(1) + acc}

为什么调用 Int -> Int lambda 会返回 Unit?以及如何完成这项工作

编辑:

奇怪的是,这行得通:

val res = funcs.fold(0){acc, func -> func(0) + acc}

【问题讨论】:

    标签: kotlin functional-programming


    【解决方案1】:

    我怀疑您将 reduce() 函数与 fold() 函数混淆了。

    两者都是高阶函数,它们将操作连续应用于列表(或数组)中的值,以将它们组合成一个结果。

    reduce() 是更简单的函数,其最终结果与列表元素的类型相同。这意味着该操作必须接受两个相同类型的值,并返回一个相同类型的值。*  例如,您可以通过调用 reduce() 来添加整数列表并给出加法函数——将两个整数相加得到另一个整数,因此部分和和最终和也是整数。

    fold() 是一个更通用的函数,其中最终结果可以是不相关的类型。这使它更强大,但也更复杂一些。您给出的操作必须接受一个列表类型的参数,但另一个(累加器)的结果类型,它必须返回结果类型。而且因为它不能像reduce() 一样“引导”,所以你还需要给累加器一个初始值(例如,0 表示加法)。

    在你的情况下,我不完全确定你想要实现什么(除了学习高阶函数,这当然是一个完全有效的目标!),所以我没有“解决方案” ' 一样。

    您的列表包含函数,因此reduce() 也必须返回一个函数。这是可能的,但是组合函数来提供另一个函数很复杂,例如:

    val res = funcs.reduce{ acc, elem -> { i -> elem(i) + acc(i) } }
    

    但是,fold() 也可以工作 [正如我从您的编辑中看到的,您已经发现] 并且看起来更接近您的目标:

    val res = funcs.fold(0){ acc, elem -> elem(1) + acc }
    

    (* 其实reduce()的结果类型可以是元素类型的超类型,从签名可以看出:inline fun &lt;S, T : S&gt; Iterable&lt;T&gt;.reduce(operation: (acc: S, T) -&gt; S): S. 但不能是不相关的类型。)

    【讨论】:

    • 很酷,所以 reduce 必须返回与 inputlist 包含的类型相同的类型?
    • (见我刚刚添加的脚注。)
    【解决方案2】:

    reduce 函数接受一个 lambda(函数),该 lambda 函数接受两个参数:accit,并返回一个与 acc 相同类型的值

    public inline fun &lt;S, T : S&gt; Iterable&lt;T&gt;.reduce(operation: (acc: S, T) -&gt; S): S

    在您的情况下,accelem 都是返回整数但在您的代码中您没有返回任何内容的函数

    这是修改后的代码

    val res1 = funcs.reduce { acc, elem ->
        { 
            elem(1) + acc(1) 
        }
    }
    

    在 Kotlin lambda 函数中,返回值仅在 lambda 末尾提及,您不需要使用 return

    【讨论】:

      【解决方案3】:

      我不太熟悉 Kotlin,所以请原谅我的风格,但问题/答案中的某些内容引起了我的注意,因为这可能是误解的征兆。

      这里使用fold 最有意义的方式如下

      val res = funcs.fold(0){x, f -> f(x)}
      

      考虑到funcs 已经是部分应用的+ 操作的列表等待与第二个参数一起应用,您通过将01 应用于当前值来强制添加的方式有点笨拙。


      关于reduce,它的工作原理类似,唯一的区别是结果是一个将10添加到其输入的函数。

      val inc = funcs.reduce{f, g -> {x -> g(f(x))}}
      val res = inc(2) // 12
      

      问题是reduce 在输入数组为空时抛出。 fold 为我们解决了这个问题:

      val id = {x: Int -> x}
      val inc = funcs.fold(id){f, g -> {x -> g(f(x))}}
      

      现在,如果数组为空,inc 将简单地吐出它的参数而不受影响。

      请注意,我不应该强制转换 x: Int。这就是我的 Kotlin foo 崩溃的地方:lambdas 不能有泛型类型,常规函数不能被传递(或者看起来如此)。

      如果你没有找到解决这个问题的方法,你将很难在 Kotlin 中进行函数式编程。


      如果你想了解更多关于reduce/fold的知识,这里有一个值得更多爱的深入答案here。使用的语言是 Javascript,但讨论更笼统。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-07
        • 2019-11-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多