我可以将任意函数传递给Scala中的另一个函数吗?

时间:2011-06-14 19:48:50

标签: function scala functional-programming polymorphism arity

我是Scala的新手,能够将函数传递给其他函数非常简洁 - 但是我可以将任意函数引用传递给另一个函数吗?所述功能参数的arity将是固定的(也就是说,我也很好奇你是否可以传递任意arity的函数)。我不断遇到类型错误。我尝试过使用Any,但似乎没有帮助。

例如,我有以下代码:

class CodeRunner(val user_defined: (Int) => Unit) {
  def run(input: Int) = {
    user_defined(input)
  }
}

def arbitrary_code(input: Int) = { println("Running with input " + input) }

val d1 = new CodeRunner(arbitrary_code)

d1.run(4)

我得到了:

Running with input 4

现在,让我们说我想传递以下函数:

def arbitrary_code(input: String) = { println("Running with input " + input) }

如何更改我的CodeRunner课程以处理它们?

5 个答案:

答案 0 :(得分:9)

  

如何更改我的CodeRunner课程以处理它们?

您可以将任意类型设为类的参数:

class CodeRunner[T](val user_defined: (T) => Unit) {
  def run(input: T) = {
    user_defined(input)
  }
}

def arbitrary_code(input: Int) = { println("Running with input " + input) }

val d1 = new CodeRunner(arbitrary_code)

d1.run(4)

def arbitrary_code2(input: String) = { println("Running with input " + input) }

val d2 = new CodeRunner(arbitrary_code2)

d2.run("hello")

请注意,d2的类型为CodeRunner[String]d1无法分配给CodeRunner[Int]

答案 1 :(得分:3)

通用类型允许您定义具有占位符类型的类,该类在实例化对象时指定。编译器很高兴,因为它可以确保一切都是类型安全的,并且您很高兴,因为您可以实例化对象并为值传递任意类型。

要在类中使用泛型类型,可以像这样修改它:

class CodeRunner[T] (val user_defined: (T) => Unit) {
  def run(input: T) = {
    user_defined(input)
  }
}

“类CodeRunner”之后的[T]是重要的部分 - 它定义了一个泛型类型T(你可以用另一个大写字母替换T等),它将在类定义中使用。 / p>

所以,如果你定义一个方法:

def arbitrary_code(input: String) = { println("Running with input " + input) }

然后将其传递给:

val d1 = new CodeRunner(arbitrary_code)

...然后编译器说“aha,对于CodeRunner的这个实例,泛型类型T是一个字符串”。如果你调用

d1.run("string")

编译器会很高兴,但不会让你传入d1.run(4)。

答案 2 :(得分:2)

要传递任意函数,您当然可以使用泛型:

def run[T,U](f: T => U) = println(f)

对于任意arity,这是不可能的,因为类型T =>的函数。 U是Function1 [U,T]的实例,类型(T,U)的函数=> V是Function2 [T,U,V]的实例。 (另外,我找不到任何有用的用例)。 然而,有一个称为“currying”的聪明概念。它包含转换一个带有多个参数的函数,并在一个函数中返回一个值,该函数接受一个参数并返回另一个函数。 这是一个例子:

def nonCurriedAdd(x: Int, y: Int) = x + y
// nonCurriedAdd(4,6)
def curriedAdd(x: Int) = (y: Int) => x + y
// we can use some syntax sugar
def curriedAdd(x: Int)(y: Int) = x + y
// curriedAdd(4)(6)

所以,你现在可以做`d1.run(curriedAdd)。 您还可以使用“curried”方法转换curried函数中的非curried函数:

d1.run(nonCurriedAdd.curried)

答案 3 :(得分:1)

  

我可以将任意函数引用传递给另一个函数吗?所述功能参数的arity将是固定的

与往常一样,记下您正在开发的功能的类型,一切都变得清晰。

你的单词“任意”表示函数参数适用于任何类型。也就是说,它们是多态函数(或某些语言中的通用函数)。

以下内容应该相当干净地转换为Scala:

== The type of an "arbitrary" function of fixed arity
f :: a -> b -> c -> d

-- The type of a function that accepts such a
-- function as an argument, and does something with it:
g :: (a -> b -> c -> d) -> a -> b -> c -> d

-- And a function that implements something of that type
g f a b c = f a b c

你可能能够提出一些其他的高阶函数,它们采用固定的arity函数,但是任意(即多态)类型,并对它们进行操作。

经典的高阶函数是例如

map :: (a -> b) -> [a] -> [b]

fold :: (a -> b -> b) -> b -> [a] -> b

还有很多人。

答案 4 :(得分:0)

scala> object codeRunner {
     |    def run[InputType, OutputType](code: InputType => OutputType) = (input: InputType) => code(input)
     | }
defined module codeRunner

scala> def someCode(x: Int) {
     |    println("This code uses " + x)
     | }
someCode: (x: Int)Unit

scala> def otherCode(y: String) {
     |    println("This code uses " + y)
     | }
otherCode: (y: String)Unit

scala> codeRunner.run(someCode)(10)
This code uses 10

scala> codeRunner.run(otherCode)("hello")
This code uses "hello"