【问题标题】:In Scala, is a function that takes call by name parameter different from a function that takes another function as parameter?在 Scala 中,按名称参数调用的函数与将另一个函数作为参数的函数不同吗?
【发布时间】:2014-11-19 02:27:11
【问题描述】:

首先,我有这个功能:

def number5()={
      println("number 5 starting")
      println("number 5 exiting")
      5
}

然后:

def giveMeCallByNameParameter(f: =>Int)={
      println("starting")
      f
      println("exiting")
}

当我调用它时:

giveMeCallByNameParameter(number5)

我得到这个结果:

starting
number 5 starting
number 5 exiting
exiting

如果我也有这个功能:

def giveMeAnotherFunction(f: ()=>Int)={
      println("starting")
      f()
      println("exiting")
}

我称之为:

giveMeAnotherFunction(number5)

我得到相同的结果:

starting
number 5 starting
number 5 exiting
exiting

那么,它们有什么不同吗?除了有没有括号的区别?

如果他们没有不同?那我们为什么要按名称调用这个术语呢?

【问题讨论】:

  • 注意:这些是方法,不是函数。事实上,按名参数和作为函数的参数的区别之一是函数不能有按名参数,只有方法可以。

标签: scala functional-programming terminology higher-order-functions callbyname


【解决方案1】:

按名称参数可以是任何有效的表达式。函数也是有效的表达式,但只是一种表达式。

按名称参数和按值参数的最大区别在于,按值参数是最常见的一种函数参数,在传递给函数之前进行评估。按名称参数的评估至少要延迟到被传递给函数之后。函数本身可能会或可能不会评估参数,它没有义务。

函数恰好具有相同的属性,但同样,正如我之前所说,函数只是一种表达式,而名称参数可以采用任何类型的有效表达式。

按名称参数的一个很好的用例是构建自定义断言函数:

def byNameAssert(predicate: => Boolean) =
  if (assertionsEnabled && !predicate)
    throw new AssertionError

这样,您可以通过控制assertionsEnabled 值来关闭对断言条件的评估。

如果没有启用断言,你甚至可以有一个表达式,它通常会抛出,而不是产生异常:

byNameAssert(x / 0 == 0)

还要注意表达式x / 0 == 0 不是函数! By-name 参数可以采用任何类型的表达式,但至少会在调用函数之后对其进行评估。

希望这会有所帮助!

【讨论】:

  • 我明白了,我可以这样做:giveMeCallByNameParameter(5) 并按名称调用。但它的实用价值是什么?
  • 你也可以使用by-name参数来实现所谓的“懒惰”功能,如这个答案所示:stackoverflow.com/a/3566978/114359
【解决方案2】:

在这个特定的例子中,两者看起来是一样的。但是考虑一下这个用例

giveMeCallByNameParameter(number5 * number5)

你得到这个结果:

开始
5号开始
5号退出
5号开始
5号退出
退出

如果您尝试在 giveMeAnotherFunction 上执行相同操作,它将无法编译

scala> giveMeAnotherFunction(number5() * number5())
:10: 错误:类型不匹配;
发现:Int
必需:() => 整数
giveMeAnotherFunction(number5() * number5())

你必须发送一个函数,而不仅仅是任何表达式

giveMeAnotherFunction(() => number5 * number5)

【讨论】:

    【解决方案3】:

    据我所知,至少在您的示例中没有区别。

    【讨论】:

    • 那么使用这个术语似乎只会增加混乱
    • 还有其他例子可以说明差异吗?
    • @CuiPengFei 我已经问过这个问题了,我想你想要的例子是函数接受参数的时候。
    【解决方案4】:

    有区别。

    • 按名称调用仅在您使用它时进行评估,它也期望一个 价值
    • 另一方面,函数是映射而不是

    因此,将方法number5 更改为函数,不能使用按名称调用,但可以在函数中使用

    def number5()={
      println("number 5 starting")
      println("number 5 exiting")
      5
    }
    
    def func: () => Int = number5 // change method to function
    
    def giveMeCallByNameParameter(f: =>Int)={
      println("starting")
      f
      println("exiting")
    }
    
    giveMeCallByNameParameter(func) //compilation error
    
    def giveMeAnotherFunction(f: ()=>Int)={
      println("starting")
      f()
      println("exiting")
    }
    
    giveMeAnotherFunction(func) // this is fine
    

    另外,你可以在需要functiongiveMeAnotherFunction中使用method,是因为ETA-expansion强>。有很多使用它的例子,比如map、foldLeft等等。

    【讨论】:

      猜你喜欢
      • 2018-07-30
      • 1970-01-01
      • 2017-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多