Scala - 前缀一元运算符

11

最近我重新开始学习Scala,并用它实现了一个我在函数式或伪函数式语言中经常做的项目:一个命题逻辑(后来扩展到谓词逻辑)的自动推理程序。

现在,我尝试使语言本身的命题逻辑表示尽可能美观,并且通过隐式转换(String -> Atom)已经达到了这个程度:

("A" and "B") implies "C"

"and"和"implies"(以及"or"和"equivalent")函数是调用相关案例类构造函数的简单方法。 然而,当实现"not"时,我陷入了以下两种符号之一:

("A" and "B").not
Not("A" and "B")

有没有办法欺骗Scala接受所期望的内容:

not("A" and "B")

最好不要将类名"Not"更名为"not",因为我将来可能想将其命名为"¬"或其他名称。

4个回答

23

我注意到在这个答案中,似乎可以在运算符名前加上unary_来实现你尝试做的事情。 (请参见unary_ !。)

编辑:这篇文章证实了这个语法。


2
但是这并不适用于自定义运算符名称。也就是说,您不能定义 unary_not 来获取前缀“not”运算符。 - sepp2k
20
具体来说,prefix_ 只能与 !~+- 一起使用。 - sepp2k
1
@sepp2k:谢谢,我没有意识到这个细微之处。这个限制似乎有点可惜(而且完全是武断的)。 - Paul Ruane
15
@Paul: 我认为这不是那么随意的。我想如果没有这个限制,解析会变得非常麻烦。在类型检查之前,你无法确定 foo bar 应该解析成 foo.bar() 还是 bar.unary_foo() (即使在两种情况都可能的情况下,你仍然需要规则来决定应该选择哪一个)。 - sepp2k
不过,这意味着我可以将其实现为“!”或“〜”运算符,这正是我想要的。 - wen
2
如果你选择使用 ! 而不是 not,请记住 Scala 还允许你定义 &&||->,因此你不必混合运算符和单词。 - sepp2k

14

你可以在单例对象上定义一个名为not的方法,像这样:

object Logic {
  def not(x:Expr) = Not(x)
}
import Logic._
not("A" and "B")

(其中 Expr 应该是 AndOrNotAtom 的公共超类)

编辑: 这里有一个例子,说明如何只使用一个导入:

object Logic {
  abstract class Expr {
    def and(e: Expr) = Conjunction(this, e)
    def or(e: Expr) = Disjunction(this, e)
    def implies(e: Expr) = Implication(this, e)
  }
  case class Conjunction(e1: Expr, e2: Expr) extends Expr
  case class Disjunction(e1: Expr, e2: Expr) extends Expr
  case class Implication(e1: Expr, e2: Expr) extends Expr
  case class Negation(e: Expr) extends Expr
  case class Atom(name: String) extends Expr

  def not(e: Expr) = Negation(e)
  implicit def string2atom(str: String) = Atom(str)
}

// use site
import Logic._
not("A" and "B") implies (not("A") or not("B"))

谢谢,我不知道在Scala中可以使用“静态导入” - 这将使我每个页面都必须进行强制导入,虽然这与隐式转换一起会产生大量的额外代码。 - wen
2
@Dennetik:如果您将所有内容都放入Logic对象中,则只需要使用import Logic._即可使用您的类。 - sepp2k
1
没想到,我还需要适应Scala相对于Java的自由度... - wen
您还可以将函数直接放入包中,与类并排放置,因此无需定义“Logic”包装对象;请参阅package objects - Erik Kaplun

8

为什么不使用Not而不是not?没有任何阻止你这样做的理由:

object not {
  def apply(expr: T) = ...
}

And then use not("A" and "B").


为什么每个人都在寻找绕过将函数直接放入包中使用包对象的方法? - Erik Kaplun
2
@ErikAllik 因为在2010年还没有包对象。 - Daniel C. Sobral
2
好观点!我没意识到—谢谢:) 另外,你会在今天使用一个来定义 not(expr: T) 吗? - Erik Kaplun

5
截至2014年2月,我认为在避免所有额外的混乱/包装的情况下,在表达式上定义类似前缀“not”的操作最清晰的方法是直接在包范围内声明函数,以及您的所有其他函数、类、类型等:这可以通过定义包对象来完成(Scala不允许您将函数放在.scala文件的根级别(我很想知道为什么——这只是为了追随Java的脚步吗?))。
package org.my.logiclib

implicit class Atom(s: String) { ... }
class MyType1
class MyType2

object `package` {
  def not(expr: Expr) = ...
}

这样做,使用 import org.my.logiclib._ 将会导入所有内容,包括 not()

以上与以下代码等效:

package org.my

package logiclib {
  implicit class Atom ...
  ...

  def not(expr: Expr) = ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接