7

我是 Scala 的新手,能够将函数传递给其他函数非常简洁——但是我可以将任意函数引用传递给另一个函数吗?所述功能参数的元数将是固定的(也就是说,我也很好奇您是否也可以传递具有任意元数的函数)。我一直被类型错误绊倒。我试过使用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班级来处理这两个问题?

4

5 回答 5

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")

请注意,d2is CodeRunner[String]which 的类型不能分配给d1which is CodeRunner[Int]

于 2011-06-14T20:00:02.273 回答
3

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

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

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

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

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

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

然后传入:

val d1 = new CodeRunner(arbitrary_code)

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

d1.run("string")

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

于 2011-06-14T20:09:16.797 回答
2

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

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

对于任意数量,这是不可能的,因为 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”方法将非咖喱函数转换为咖喱函数:

d1.run(nonCurriedAdd.curried)
于 2011-06-15T06:18:37.667 回答
1

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

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

您的“任意”一词表明函数参数适用于任何类型。也就是说,它们是多态函数(或泛型函数,在某些语言中)。

以下应该相当干净地转换为 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

您可能会想出一些其他这样的高阶函数,它们采用固定数量但任意(即多态)类型的函数,并对它们进行操作。

经典的高阶函数例如

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

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

还有很多很多其他的。

于 2011-06-14T19:59:14.890 回答
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"
于 2011-06-14T23:03:39.830 回答