4

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

由于它的灵活性,我会考虑使用 Scala 作为脚本语言。但即使使用类型推断,所有和必需的val类型var定义对于习惯于像 matlab 这样的动态语言的外行用户来说还是太多了。相比之下,Boo 的 option-ducky选项可能会起作用,但我想留在 JVM 而不是 .NET 上。我认为 Scala 没有-ducky

更一般地,考虑以下 DSL(来自http://www.cs.utah.edu/~hal/HBC/)来模拟潜在狄利克雷分配:

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 这样的基本脚本语言?

想法赞赏。

4

3 回答 3

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

在这种特殊情况下,几乎什么都没做,除了 define GamVecNorMV等,并从SymboltoInt或创建一个隐式定义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)) 
)

在这种情况下GamMult需要对 , 等进行一些不同的定义,以处理传递给它们的符号。不过,过多的“'”肯定很烦人。

这并不是说 HBC 没有它自己的特性,例如偶尔需要类型声明、索引前的下划线、偶尔需要将“ ~”替换为“ \in”,甚至需要在后面加上反斜杠。只要使用它而不是 HBC、MathLab 或人们习惯的任何其他东西有真正的好处,他们就会给自己带来一些麻烦。

于 2009-08-19T18:51:50.423 回答
0

编辑:

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

我不确定您要达到的目标。您的脚本语言会更像“什么”还是“如何”类型?您给我的示例是“什么”类型的 DSL -> 您描述了您要实现的目标,而不关心实现。这些是最适合用来描述问题的语言,并且根据您正在为其构建应用程序的领域,我认为这是最好的方法。用户只需使用对问题域非常熟悉的语法来描述问题,应用程序会解析此描述并将其用作输入以运行模拟。为此,构建语法并使用 scala 解析实用程序对其进行解析可能是最好的方法(您只想为用户公开一小部分功能)。

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

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

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

--以下部分讨论可能的脚本语言。我上面的回答建议不要使用它们,但为了完整起见,我已经离开了讨论。

我没有这方面的经验,但看看Groovy。它是一种用于 JVM 的动态类型脚本语言(由于 JDK 7 对 JVM 的支持可能会变得更好invokedynamic)。它还对运算符重载编写 DSL有很好的支持。不幸的是,它不支持用户定义的运算符,至少据我所知不支持。

不过,我仍然会使用 scala(部分原因是我喜欢静态类型,而且我发现它的类型推断很好:)。它的脚本支持非常好,你可以让几乎任何东西看起来像原生语言支持(例如看看它的演员库!)。它还对函数式编程有很好的支持,可以使脚本非常简短和简洁。作为一个好处,您可以使用 Java 库的所有功能。

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

于 2009-08-19T16:19:40.107 回答
0

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

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

于 2009-08-19T20:12:31.820 回答