Scala : 交互式应用程序,函数组合
函数和交互式应用程序
回调(callback)在交互式应用程序中是很常见的。例如一个按钮被点击,并进行一个特定的动作。在Web 应用程序中创建回调是尤其困难的,除非你拥有像Scala这样强大的工具。下面创建一个方法,它生成一个随机的String,以作为一个全局唯一的标识符(GUID)。
scala> def randomName() = "I"+ scala.util.Random.nextLong.abs randomName: ()java.lang.String
然后定义一个通用的JavaScript trait, 我们不需要刷新它,假设它包含可以在浏览器中运行的JavaScript 命令。
scala> trait JavaScript defined trait JavaScript
下面用一个能生成 JavaScript的函数来创建一个Map 以关联GUID:
scala> var callbacks: Map[String, () => JavaScript] = Map() callbacks: Map[String,() => JavaScript] = Map()
scala> def register(f: () => JavaScript) = {
| val name = randomName
| callbacks += name -> f
| <button onclick={"invokeServerCall("""+name+""")"}>ClickMe</button>
| }
register: (f: () => JavaScript)scala.xml.Elem
当用户点击按钮时,会用 GUID 创建一个Ajax HTTP 请求,Servlet 在map中查找GUID,如果GUID存在,这个函数就会被调用并将JavaScript则返回给浏览器,这个功能的代码类型于:
Scala> def handleAjax(guid: String): HttpResponse =
functionMap.get(guid).map(f => f()) match {
case Some(javaScript) => JavaScriptResponse(javaScript)
case _ => Http404Response()
}
上面的代码是 Lift 框架的简化版本,可以看到,这种写法可以让程序员更关注于业务逻辑。
构建新的函数
我们能以其它函数为基础构建新的函数,函数组合提供了很强大的功能(例如parser combinator),现在来看一下解析一系列的命令和编译一个解析它们的函数的区别。
首先,定义一个語法,在这个語法中,表达式可以是常量值或命名变量。表达式可以加或者乘其它表达式。
下面是描述这个語法的case classes:
sealed trait Expr case class Add(left: Expr, right: Expr) extends Expr case class Mul(left: Expr, right: Expr) extends Expr case class Val(value: Int) extends Expr case class Var(name: String) extends Expr
解析表达式:
scala> def calc(expr: Expr, vars: Map[String, Int]): Int = expr match {
| case Add(left, right) => calc(left, vars) + calc(right, vars)
| case Mul(left, right) => calc(left, vars) * calc(right, vars)
| case Val(v) => v
| case Var(name) => vars(name)
| }
calc: (expr: Expr,vars: Map[String,Int])Int
在这里,expr是要计算的表达式,而vars则是一个包含了我们的变量的Map。下面将这个方法调用转化为一个函数:
scala> def buildCalc(expr: Expr): Map[String, Int] => Int = expr match {
| case Add(left, right) =>
| val lf = buildCalc(left)
| val rf = buildCalc(right)
| m => lf(m) + rf(m)
| case Mul(left, right) =>
| val lf = buildCalc(left)
| val rf = buildCalc(right)
| m => lf(m) * rf(m)
| case Val(v) => m => v
| case Var(name) => m => m(name)
| }
buildCalc: (expr: Expr)(Map[String,Int]) => Int
buildCalc方法返回一个函数,它能传递给其它函数。JVM能够优化这个组合函数,比解析的版本性能更好,这是因为组合函数没有多余的元素与模式匹配相关联。函数是通过不断调用函数的apply方法来计算的,因此,每个节点的代价是一或两个方法的分发而不是整个模式匹配。
Related posts:
评论