19

仍然不清楚的是,在延迟评估和其他好处(如果有的话)方面,名称参数比匿名函数有什么优势:

def func1(a: => Int)
def func2(a: () => Int)

我应该什么时候使用第一个,什么时候使用第二个?

这不是What's the difference between => , ()=>, and Unit=>的副本

4

5 回答 5

9

懒惰在这两种情况下是相同的,但有细微的差别。考虑:

def generateInt(): Int = { ... }
def byFunc(a: () => Int) { ... }
def byName(a: => Int) { ... }

// you can pass method without
// generating additional anonymous function
byFunc(generateInt)

// but both of the below are the same
// i.e. additional anonymous function is generated
byName(generateInt)
byName(() => generateInt())

但是,按名称调用的函数对于制作 DSL 很有用。例如:

def measured(block: ⇒ Unit): Long = {
  val startTime = System.currentTimeMillis()
  block
  System.currentTimeMillis() - startTime
}

Long timeTaken = measured {
  // any code here you like to measure
  // written just as there were no "measured" around
}
于 2015-03-07T19:43:04.487 回答
6
  def func1(a: => Int) {
    val b = a // b is of type Int, and it`s value is the result of evaluation of a
  }

def func2(a: () => Int) {
    val b = a // b is of type Function0 (is a reference to function a)
  }
于 2013-11-13T11:20:19.160 回答
6

一个例子可能会非常彻底地了解这些差异。

考虑一下您想在 Scala 中编写自己的真正while循环版本。我知道,我知道……while在 Scala 中使用?但这与函数式编程无关,这是一个很好地展示该主题的示例。所以和我在一起。我们将调用我们自己的版本whyle。此外,我们希望在使用 Scala 的 builtin 的情况下实现它while。为了实现这一点,我们可以使我们的whyle构造递归。此外,我们将添加@tailrec注释以确保我们的实现可以用作内置while. 这是第一次尝试:

@scala.annotation.tailrec
def whyle(predicate: () => Boolean)(block: () => Unit): Unit = {
  if (predicate()) {
    block()
    whyle(predicate)(block)
  }
}

让我们看看这是如何工作的。我们可以将参数化的代码块传递给whyle. 第一个是predicate参数化函数。第二个是block参数化函数。我们将如何使用它?

我们想要的是让我们的最终用户whyle像使用while控制结构一样使用:

// Using the vanilla 'while'
var i = 0
while(i < args.length) {
  println(args(i))
  i += 1
}

但是由于我们的代码块是参数化的,我们循环的最终用户whyle必须添加一些丑陋的语法糖来让它工作:

// Ouch, our whyle is hideous
var i = 0
whyle( () => i < args.length) { () =>
  println(args(i))
  i += 1
}

所以。看来,如果我们希望最终用户能够以whyle更熟悉的原生风格调用我们的循环,我们将需要使用无参数函数。但是我们遇到了一个非常大的问题。一旦你使用无参数函数,你就不能再吃蛋糕了。你只能吃你的蛋糕。看哪:

@scala.annotation.tailrec
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
  if (predicate) {
    block
    whyle(predicate)(block)  // !!! THIS DOESN'T WORK LIKE YOU THINK !!!
  }
}

哇。现在用户可以whyle按照他们期望的方式调用我们的循环......但是我们的实现没有任何意义。您无法同时调用无参数函数并将函数本身作为值传递。你只能调用它。这就是我说的只吃你的蛋糕的意思。你也不能拥有。因此,我们的递归实现现在退出了窗口。它只适用于参数化函数,不幸的是它非常难看。

在这一点上,我们可能会想作弊。我们可以重写我们的whyle循环以使用 Scala 的内置while

def whyle(pred: => Boolean)(block: => Unit): Unit = while(pred)(block)

现在我们可以使用我们的whyle完全一样的while,因为我们只需要能够吃我们的蛋糕……我们也不需要拥有它。

var i = 0
whyle(i < args.length) {
  println(args(i))
  i += 1
}

但是我们作弊了!实际上,这是一种拥有我们自己的尾部优化版本的 while 循环的方法:

def whyle(predicate: => Boolean)(block: => Unit): Unit = {
  @tailrec
  def whyle_internal(predicate2: () => Boolean)(block2: () => Unit): Unit = {
    if (predicate2()) {
      block2()
      whyle_internal(predicate2)(block2)
    }
  }
  whyle_internal(predicate _)(block _)
}

你能弄清楚我们刚刚做了什么吗?我们在这里的内部函数中有我们原来的(但丑陋的)参数化函数。我们用一个函数包装了它,该函数将无参数函数作为参数。然后它调用内部函数并将无参数函数转换为参数化函数(通过将它们转换为部分应用函数)。

让我们尝试一下,看看它是否有效:

var i = 0
whyle(i < args.length) {
  println(args(i))
  i += 1
}

确实如此!

谢天谢地,因为在 Scala 中我们有闭包,所以我们可以大量清理它:

def whyle(predicate: => Boolean)(block: => Unit): Unit = {
  @tailrec
  def whyle_internal: Unit = {
    if (predicate) {
      block
      whyle_internal
    }
  }
  whyle_internal
}

凉爽的。无论如何,这些是无参数函数和参数化函数之间的一些非常大的区别。我希望这能给你一些想法!

于 2015-06-24T19:32:10.967 回答
1

这两种格式可以互换使用,但在某些情况下我们只能使用其中一种主题。

让我们通过例子来解释,假设我们需要定义一个带有两个参数的案例类:

{
  .
  .
  .
  type Action = () => Unit;

  case class WorkItem(time : Int, action : Action);
  .
  .
  .

}

正如我们所见,WorkItem 类的第二个参数有一个 Action 类型。

如果我们尝试将此参数替换为其他格式=>

case class WorkItem1(time : Int, s : => Unit) 编译器将显示消息错误:

此行有多个标记:

`val' 参数可能不是按名称调用的 按名称调用参数创建:() ⇒</p>

所以我们看到格式()=>更通用,我们可以使用它来定义 Type,作为类字段或方法参数,另一方面=>格式可以用作方法参数但不能用作类字段。

省略了空参数列表 () 的按名称类型仅允许用于参数。没有名称变量或名称字段之类的东西。

于 2016-02-25T15:06:29.897 回答
0

如果要按Int名称作为参数传递,则应使用第一个函数定义。如果您希望参数是返回 . 的无参数函数,请使用第二个定义Int

于 2013-11-13T09:24:15.650 回答