【问题标题】:Can I pass an arbitrary function to another function in Scala?我可以将任意函数传递给 Scala 中的另一个函数吗?
【发布时间】:2011-09-15 00:08:58
【问题描述】:

我是 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 类以同时处理两者?

【问题讨论】:

  • 闻起来像仿制药:scala-lang.org/node/113
  • 呃。 smacks head 这在有人指出之后似乎很明显。谢谢!

标签: function scala functional-programming polymorphism arity


【解决方案1】:

如何更改我的 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]

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      对于任意数量,这是不可能的,因为 T => U 类型的函数是 Function1[U,T] 的实例,而 (T,U) => V 类型的函数是 Function2[T,U,五]。 (另外,我找不到任何有用的用例)。 但是,有一个聪明的概念叫做“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)
      

      【讨论】:

        【解决方案4】:

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

        和往常一样,写下你正在开发的函数的类型,一切就变得清晰了。

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

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

        还有很多很多其他的。

        【讨论】:

          【解决方案5】:
          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"
          

          【讨论】:

            猜你喜欢
            • 2015-12-14
            • 1970-01-01
            • 2010-09-11
            • 2021-02-04
            • 2022-06-12
            • 2012-09-24
            • 2021-03-28
            • 2012-12-05
            • 1970-01-01
            相关资源
            最近更新 更多