【问题标题】:Writing a function to curry any function编写一个函数来柯里化任何函数
【发布时间】:2019-04-06 22:46:11
【问题描述】:

为了记录,我发现函数在 Scala 中不能自动柯里化,这很烦人。我正在尝试编写一个接受任何函数并返回咖喱版本的工厂:

def curry(fn:(_ => _)) = (fn _).curried

基本上我在这里定义的是一个函数curry,它以fn 类型的函数_ => _ 作为参数,并返回函数fn 的柯里化版本。显然这不起作用,因为 Java。

这是我得到的错误:

error: _ must follow method; cannot follow fn.type
       def curry(fn:(_ => _)) = (fn _).curried

那里的任何大师可以帮助我弄清楚为什么这不起作用?我并不是说听起来刻薄,我习惯于将所有类型都视为函数的函数式语言。请帮助这个 Scala 新手。

(我用 haskell 标记了这个问题,因为我试图让 Scala 函数表现得像 Haskell 函数:'(

更新

澄清一下,我需要一个 curryN 函数,所以这个函数可以对任何其他函数进行柯里化,而不管它的数量如何。

旁注,有人指出增加 fn 参数的数量可以解决问题。没有:

def curry2(fn:((_, _) => _)) = (fn _).curried
error: _ must follow method; cannot follow fn.type
       def curry2(fn:((_, _) => _)) = (fn _).curried

【问题讨论】:

  • 你不能curryFunction1
  • 请解释一下这是什么意思
  • 我不知道任何 Scala,但我认为这意味着你需要像 curry(fn:((_,_) => _)) = (fn _).curried 这样的东西。
  • _ => _Function1( _ , _ ) => _Function2 等等。
  • 我需要 curry 一个有 N 个参数的函数。这就是我试图描述的......请查看更新问题

标签: scala haskell functional-programming


【解决方案1】:

Scala 不允许您对函数的数量进行抽象。因此,您需要使用类型类风格的方法(在您完成所有手动工作之后,它允许您抽象任何东西)。

所以,特别是,你会做类似的事情

sealed trait FunctionCurrier[Unc, Cur] { def apply(fn: Unc): Cur }
final class Function2Currier[A, B, Z]
extends FunctionCurrier[(A, B) => Z, A => B => Z] {
  def apply(fn: (A, B) => Z): (A => B => Z) = fn.curried
}
// Repeat for Function3 through Function21

implicit def makeCurrierForFunction2[A, B, Z]: Function2Currier[A, B, Z] =
  new Function2Currier[A, B, Z]
// Again, repeat for Function3 through Function21

def curryAll[Unc, Cur](fn: Unc)(implicit cf: FunctionCurrier[Unc, Cur]): Cur =
  cf(fn)

现在你可以像这样使用它了:

scala> def foo(a: Int, b: String) = a < b.length
foo: (a: Int, b: String)Boolean

scala> curryAll(foo _)
res0: Int => (String => Boolean) = <function1>

Shapeless 中可能已经有类似的东西了,但在这种情况下,你可以自己动手,尽管有些乏味(和/或代码生成器)。

(注意:如果你想“curry”A =&gt; Z,你可以写一个Function1Currier,它只返回原样的函数。)

【讨论】:

  • 这是……非常冗长。作为一个scala新手,你能澄清一些事情吗?什么是sealed 特征?为什么有些参数在方括号中?什么是无形?
  • 你试图在你能走路之前跑。如果您真的不了解类型参数(“方括号中的参数”)是什么,那么尝试编写通用函数currier 可能还为时过早。无形:github.com/milessabin/shapeless
  • @dopatraman - 保罗的权利。您要求做一些新手无法真正接触到的事情。新手级别的答案是:你不能,因为 Scala(像许多语言一样)不会抽象函数的数量(即参数的数量)。实际答案是:如果您知道自己在做什么,就可以自己编写抽象代码。从新手转变为新手的良好动力,即使它不能解决您现在遇到的问题!
【解决方案2】:

这可以使用函数的curried 方法来完成。您需要将函数本身作为部分应用函数访问并获取其柯里化形式,如下所示:

def fn(i: Int, j: Int) = i + j

val fnCurryable = (fn _).curried

val fnCurried = fnCurryable(1)
println(fnCurried(2))
//prints 3

由于 scala 强大的类型推断,相同的第二行可以用于 curry 任何具有 2-22 个参数的函数。另外,请记住,您可以在声明中声明您的函数为可curryable。这将与上述相同:

def fnCurryable(i: Int)(j: Int) = i + j

使用多个参数列表意味着这个函数被称为fnCurryable(1)(2),并且永远不能被称为fnCurryable(1, 2)。这种转换基本上就是.curried 所做的。 这是基于以下描述的功能特征:

http://www.scala-lang.org/api/2.11.8/index.html#scala.package

【讨论】:

  • 你能提供一个完整的例子吗? fn 将在此处未定义。
  • 我需要一个返回柯里化 FunctionN 的函数。这不是那个。
【解决方案3】:
def toCurry[A](f: (A, A) => A): A => A => A = x => f(x, _)
val addTwoNum = (x: Int, y: Int) => x + y
val curriedAddTwoNum = toCurry(addTwoNum)
val part1Curry = curriedAddTwoNum(5)
println(part1Curry(2))

要获得额外的数量,您只需在上述函数定义中添加额外的参数即可。

否则,您可能想要执行Can you curry a function with varargs in scala? 之类的操作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-16
    • 2016-01-01
    • 1970-01-01
    • 2021-02-07
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多