【问题标题】:Infix operators in Scala and JythonScala 和 Jython 中的中缀运算符
【发布时间】:2009-08-19 15:42:20
【问题描述】:

我正在评估面向计算的应用程序的语言,该应用程序需要为最终用户提供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/)来模拟潜在狄利克雷分配:

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

想法表示赞赏。

【问题讨论】:

  • 在阅读了此处的讨论后,我修改了我的答案,建议使用您自己定制的 DSL 并对其进行解析...根据您的问题域,我认为这是最好的方法。跨度>

标签: scala jython dsl


【解决方案1】:

就个人而言,我认为您夸大了 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 等,并创建一个从SymbolIntDouble 的隐式定义,从稍后您将在其中存储此类定义的表(例如使用 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 或人们习惯的任何其他东西有真正的好处,他们就会有点麻烦。

【讨论】:

  • 嗯。问题的作者 likes 在这种情况下他可以在 Scala 中获得的语​​法(您试图用其他东西替换的语法)。您提议的替换中的defs 与他希望避免的“所有valvar”属于同一类别。
  • 你误读了他。如果您点击链接,他显示的语法是由称为 HBC 的东西提供的。如果可能的话,他希望拥有那个。现在,我正在准确地解决你引用的他的反对意见,声称它并没有他想的那么糟糕。
  • 没错,对不起。尽管如此,很多def 对他来说可能并不比' 好多少。
  • 这非常接近,尽管我认为 Scala 不是一个选项。用户想要基本内容的动态类型语言。除了基本的动态语言(ala matlab)之外,我还希望能够定义内部 DSL,如 HBC。似乎没有办法,只能自己解析。
【解决方案2】:

编辑:

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

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

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

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

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

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

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

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

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

【讨论】:

【解决方案3】:

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

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

【讨论】:

  • JavaScript 什么时候成为 JVM 语言的?
  • 1997 年,Rhino ECMAScript 实现被开发出来。从 Java 6 开始,Rhino 实际上是 JRE(或至少是 JDK)的标准部分。
  • JVM 语言是编译成 JVM 字节码的语言。 Rhino 不会那样做,是吗?
猜你喜欢
  • 1970-01-01
  • 2014-07-16
  • 1970-01-01
  • 2015-09-22
  • 2011-11-28
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多