按名称参数与匿名函数

时间:2013-11-13 08:13:15

标签: scala

目前尚不清楚的是,在惰性评估和其他好处(如果有)方面,匿名函数的名称参数优势是什么:

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

我应该何时使用第一个和第二个?

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

的副本

5 个答案:

答案 0 :(得分: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())

具有call-by-name的功能对于制作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
}

答案 1 :(得分:5)

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

答案 2 :(得分:4)

一个例子可能会对差异进行彻底的探讨。

请考虑您想在Scala中编写自己的版本while循环。我知道,我知道......在Scala中使用while?但这不是关于函数式编程的,这是一个很好地演示该主题的例子。跟我说吧。我们会调用自己的版本whyle。此外,我们希望使用Scala的内置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
}

冷却。无论如何,这些是无参数和参数化函数之间的一些非常大的差异。我希望这会给你一些想法!

答案 3 :(得分:1)

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

让我们举个例子来解释一下,假设我们需要用两个参数定义一个case类:

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

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

}

我们可以看到,WorkItem类的第二个参数具有Action类型。

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

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

  

此行有多个标记:

     

`VAL&#39;参数可能不是按名称调用Call-by-name参数   创作:()⇒

因为我们看到格式()=&gt; 更通用,我们可以用它来定义Type,作为类字段或方法参数,在另一边 =&gt ; 格式可以用作方法参数,但不能用作类字段。

  

仅列出空参数列表()的名字类型   允许参数。没有名字变量或者   按姓名字段。

答案 4 :(得分:0)

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

,请使用第二个定义