Scala和Jython中的中缀运营商

时间:2009-08-19 15:42:20

标签: scala jython dsl

我正在为面向计算的应用程序评估语言,该应用程序需要为最终用户提供 easy 嵌入式脚本语言。我一直在考虑使用Scala作为主要底层语言,使用Jython作为脚本界面。 Scala的一个吸引力是我可以为矩阵对象的元素乘法定义:*等方法,并将其与中缀语法a :* b一起使用。但是:*不是Python中有效的方法名称。 Jython如何处理这个问题?

由于其灵活性,我会考虑使用Scala作为脚本语言。但即使使用类型推断,所有valvar以及所需的类型定义对于习惯于像matlab这样的动态语言的非常规用户来说太多了。相比之下,Boo有选项-ducky可能有用,但我想留在JVM而不是.NET。我假设Scala没有-ducky

更一般地说,考虑以下DSL(来自http://www.cs.utah.edu/~hal/HBC/)来模拟Latent Dirichlet分配:

model {
      alpha     ~ Gam(0.1,1)
      eta       ~ Gam(0.1,1)
      beta_{k}  ~ DirSym(eta, V)           , k \in [1,K]
      theta_{d} ~ DirSym(alpha, K)         , d \in [1,D]
      z_{d,n}   ~ Mult(theta_{d})          , d \in [1,D] , n \in [1,N_{d}]
      w_{d,n}   ~ Mult(beta_{z_{d,n}})     , d \in [1,D] , n \in [1,N_{d}]
}

result = model.simulate(1000)

对于熟悉分层贝叶斯建模的用户来说,这种语法非常好(例如与PyMCMC相比)。 JVM上是否有任何语言可以很容易地定义这样的语法,并且可以访问像python这样的基本脚本语言?

赞赏的想法。

3 个答案:

答案 0 :(得分:2)

就个人而言,我认为你夸大了Scala的开销。例如,这个:

alpha     ~ Gam(10,10)
mu_{k}    ~ NorMV(vec(0.0,1,dim), 1, dim)     , k \in [1,K]
si2       ~ IG(10,10)
pi        ~ DirSym(alpha, K)
z_{n}     ~ Mult(pi)                          , n \in [1,N]
x_{n}     ~ NorMV(mu_{z_{n}}, si2, dim)       , n \in [1,N]

可以写成

def alpha =                   Gam(10, 10)
def mu    = 1 to 'K map (k => NorMV(Vec(0.0, 1, dim), 1, dim)
def si2   =                   IG(10, 10)
def pi    =                   DirSym(alpha, 'K)
def z     = 1 to 'N map (n => Mult(pi))
def x     = 1 to 'N map (n => NormMV(mu(z(n)), si2, dim))

在这种特殊情况下,除了定义GamVecNorMV等之外几乎没有做任何事情,并创建从Symbol到{{1的隐式定义或者Int,从稍后存储此类定义的表中读取(例如使用等效的Double)。这种隐含的定义是这样的:

loadM

也可以这样写:

import scala.reflect.Manifest
val unknowns = scala.collection.mutable.HashMap[Symbol,(Manifest[_], Any)]()
implicit def getInt(s: Symbol)(implicit m: Manifest[Int]): Int = unknowns.get(s) match {
  case Some((`m`, x)) => x.asInstanceOf[Int]
  case _ => error("Undefined unknown "+s)
}
// similarly to getInt for any other desired type

在这种情况下,Model ( 'alpha -> Gam(10, 10), 'mu -> 'n -> NorMV(Vec(0.0, 1, dim), 1, dim) With ('k in (1 to 'K)), 'si2 -> IG(10, 10), 'pi -> DirSym('alpha, 'K), 'z -> 'n -> Mult('pi) With ('n in (1 to 'N)), 'x -> 'n -> NorMV('mu of ('z of 'n), 'si2, dim)) With ('n in (1 to 'N)) ) Gam等需要定义稍微不同,以处理传递给它们的符号。但是,过多的“'”肯定很烦人。

这并不像HBC没有它自己的特性,例如偶尔需要类型声明,索引前的下划线,偶尔需要用“Mult”替换“~”,或者甚至是后面需要的反斜杠。只要使用它而不是HBC,MathLab或其他人习惯的其他任何东西都可以带来真正的好处,它们会给自己带来麻烦。

答案 1 :(得分:0)

编辑:

阅读完所有讨论之后,可能最好的方法是定义DSL的语法,然后使用内置的scala解析实用程序解析它。

我不确定你想要实现的目标。您的脚本语言是否更像是“what”“how”类型?你给我的例子是“什么”类型的DSL - >你描述了你想要实现的目标,而不关心实施。这些是最好用于描述问题的语言,并且通过您构建应用程序的域,我认为这是最好的方法。用户只是以问题域非常熟悉的语法描述问题,应用程序解析此描述并将其用作输入以运行模拟。为此,构建语法并使用scala解析实用程序解析它可能是最好的方法(您只想为用户公开一小部分功能)。

如果你需要一个“how”脚本,那么使用已经建立的脚本语言是可行的方法(除非你想自己实现循环,基本数据结构等)。

在设计系统时,总是需要权衡利弊。这里介绍了您希望向用户公开的功能数量和脚本的简洁性。我自己,我将尽可能少地展示完成工作的功能,并以“如何”方式完成 - 如果模拟给出,用户不需要知道如何模拟它的问题正确的结果并在合理的时间内运行。

如果您向用户公开完整的脚本语言,您的DSL将只是该脚本语言中的一个小型API,用户必须学习一种完整的语言才能使用其全部功能。并且您可能不希望用户使用其全部功能(它可能会破坏您的应用程序!)。当您的应用程序不需要连接到Internet时,为什么要公开TCP套接字支持?这可能是一个可能的安全漏洞。

--以下部分讨论可能的脚本语言。我的上述答案建议不要使用它们,但我已经完成了讨论。

我对此没有经验,但请查看Groovy。它是JVM的动态类型脚本语言(由于invokedynamic,JVM支持可能会在JDK 7中变得更好)。它还对operator overloadingwriting DSLs提供了很好的支持。不幸的是,它不支持用户定义的运算符,至少在我不知情的情况下。

我仍然会使用scala(部分因为我喜欢静态类型,我发现它的类型推断很好:)。它的脚本支持非常好,您可以使几乎任何东西看起来像本地语言支持(例如,看看它的actor库!)。它还对函数式编程提供了很好的支持,可以使脚本非常简洁。作为一个好处,您可以随意使用Java库的所有功能。

要将scala用作脚本语言,只需将脚本放在以.scala结尾的文件中,然后运行scala filename.scala。有关讨论,请参阅Scala as a scripting Language,将scala与JRuby进行比较。

答案 2 :(得分:0)

JVM脚本语言(JavaScript Rhino,JRuby,Jython和Groovy)中没有明显的嫌疑人支持用户定义的运算符(您可能需要它)。范也没有。

您可以尝试将JRuby与superators gem一起使用。