Scala,将本地定义的函数传递给列表?

时间:2012-05-08 04:16:06

标签: scala programming-languages

我还没有理解以下代码片段为什么afterDelay(0) {...},本地定义的函数可以存储到议程中?有人可以帮我理解afterDelay(0) {...}函数中的run吗?

abstract class Simulation {

  type Action = () => Unit

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

  private var curtime = 0
  def currentTime: Int = curtime

  private var agenda: List[WorkItem] = List()

  private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = {
    if (ag.isEmpty || item.time < ag.head.time) item :: ag
    else ag.head :: insert(ag.tail, item)
  }

  def afterDelay(delay: Int)(block: => Unit) {
    val item = WorkItem(currentTime + delay, () => block)
    agenda = insert(agenda, item)
  }

  private def next() {
    (agenda: @unchecked) match {
      case item :: rest => 
        agenda = rest 
        curtime = item.time
        item.action()
    }
  }

  def run() {
    afterDelay(0) {
      println("*** simulation started, time = "+
          currentTime +" ***")
    }
    while (!agenda.isEmpty) next()
  }
}

2 个答案:

答案 0 :(得分:3)

afterDelay(0) {
    println(...)
}

等同于以下内容:

afterDelay(0)({
    println(...)
})

调用函数afterDelay,新的WorkItemitem)被添加到列表中,而不是函数本身。参数 block: => Unit是“按名称参数”(参见Scala Language Specification部分4.6.1):用作参数的表达式被隐式转换为“无参数方法” “(不首先评估),只要访问方法中的变量(不需要()),就会调用它。

在这种情况下,当() => block产生的函数被调用时:它会在item.action()处调用,它发生在之后的 之后新WorkItem已添加到列表中(并afterDelay返回)。

如果它被写成(接受函数参数,而不是名字/ thunk):

def afterDelay(delay: Int)(block: () => Unit) {   // take a function
  // new function will invoke function named by "block" when invoked ...
  val item = WorkItem(..., () => block())
  // or skip wrapping the function in a function ...
  // val item = WorkItem(..., block)
  ...
}

然后需要调用传递函数

afterDelay(0)(() => { // need a function
    println(...)
})

或者,替代语法,仍然是() => Unit的函数,但可以避免使用外括号:

afterDelay(0) { () => // need a function
    println(...)
} 

从SLS中提取,4.6.1按姓名参数:

  

值参数的类型可以由=>预先固定,例如x: => T。然后,这种参数的类型是无参数方法类型=> T。这表示相应的参数在函数应用程序点处评估,而是在函数中的每次使用时进行评估。也就是说,使用call-by-name评估参数。

答案 1 :(得分:1)

您将afterDelay定义为curried function。这意味着它有两个参数列表。在scala中,您可以使用(...)替换参数列表{...}周围的括号。在第二个参数列表中,您使用的是"call-by-name" parameter。每次在您的函数中使用它们时,将再次评估这些参数。一个很好的例子是here
“名称调用”参数通常用于定义您自己的控制结构。

def do(until: Int)(body: => Unit) {
  body
  if (until > 1) do(until - 1)(body)
}

do(0)(println("test"))
do(5)(println("test2"))

这是一个直到做的例子。它将打印一次test和五次test2