【问题标题】:How to simulate inheritance in Clojure?如何在 Clojure 中模拟继承?
【发布时间】:2022-01-05 05:38:34
【问题描述】:

我最近开始研究 Clojure,所以我承认我没有太多 我也没有读过所有关于它的书。尽管如此,我还是很难 是时候弄清楚如何扩展用 Clojure 编码的系统的行为了。

更具体地说,出于教育目的,前段时间我实现了 Scala 中的解析器,用于一系列小型语言——NAND-CIRC 语言 家庭,类似于 Boaz Barak 在“Introduction to Theoretical Computer Science” 中的定义。有一个 版本的语言,然后有连续的语法糖 可以添加(内联函数、用户定义函数、if/else 和 for)。

在 Scala 中——使用解析器组合库——我可以定义基本的 语法作为一个类,如下图(语义动作省略 简洁)。

import scala.util.parsing.combinator._

class NandCircParsers extends RegexParsers {

  def program: Parser[Any] = block
  
  def block: Parser[Any] = rep(command)
  
  def command: Parser[Any] = opt(statement) ~ rep(eol)
  
  def statement: Parser[Any] = assign
  
  def assign: Parser[Any] = reference ~ "=" ~ funcall
  
  def reference: Parser[Any] = outputPort | variable
  
  def funcall: Parser[Any] = identifier ~ "(" ~ actualArgs ~ ")"
  
  def actualArgs: Parser[Any] = repsep(expression, ",")
  
  def expression: Parser[Any] = inputPort | reference
  
  def variable: Parser[Any] = identifier
  
  def inputPort: Parser[Any] = "X" ~ "[" ~ index ~ "]"

  def outputPort: Parser[Any] = "Y" ~ "[" ~ index ~ "]"

  def identifier: Parser[Any] = """[_a-zA-Z$][_a-zA-Z0-9'$]*""".r
  
  def index: Parser[Any] = number
  
  def number: Parser[Any] = """[-+]?[0-9]+""".r
  
}

然后我可以创建一个 NandCircParsers 类的实例并将其用于 处理语言的 vanilla 版本。

但是NAND-CIRD语言的另一种方言允许使用内联 职能。为此,expression 的生产规则必须更改。在斯卡拉 这是一个创建子类并覆盖相关方法的问题。

class NandCircInlineParsers extends NandCircParsers {
  
  override def expression: Parser[Any] = inputPort | reference | funcall
  
}

然后我可以实例化 NandCircInlineParsers 类并使用新的 方言 无需重写所有语法,即解析器层次结构。

对于带有 if/else 语法糖的方言(仅)情况类似。

class NandCircIfParsers extends NandCircParsers {
  
  override def statement: Parser[Any] = assign | ifSttmt
  
  def ifSttmt: Parser[Any] = 
    "if" ~ expression ~ ":" ~ block ~ opt("else" ~ ":" ~ block) ~ "end"
    
}

我只需要覆盖改变的语法(方法)的产生规则 并添加新方言,我就有了新方言的解析器。

但这是在 Scala 中。现在我正在尝试使用 Clojure 实现等效的结果。

我已经实现了一个解析器组合器库,它符合以下内容 是为parsec.el Emacs Lisp parser combinator library 完成的,根据我的需要,它是 工作。

vanilla 版本的 NAND-CIRC 语言的解析器变为 像这样的。

(def EQU    (token (literal "=")))
(def COMMA  (token (literal ",")))
(def LPAR   (token (literal "(")))
(def RPAR   (token (literal ")")))
(def LBKT   (token (literal "[")))
(def RBKT   (token (literal "]")))
(def IN     (token (literal "X")))
(def OUT    (token (literal "Y")))

(def ident  (token (regex #"[_a-zA-Z$][_a-zA-Z0-9'$]*")))
(def number (token (regex #"[-+]?[0-9]+")))

(def variable ident)

(def index number)

(def input (do* IN LBKT index RBKT))

(def output (do* OUT LBKT index RBKT))

(def expr (choice input output variable))

(def actual-args (sep-by expr COMMA))

(def funcall (do* ident LPAR actual-args RPAR))

(def formal-args (sep-by var COMMA))

(def reference (either output variable))

(def assign (do* reference EQU funcall))

(def command (do* (optional assign) (many eol)))

(def statement command)

(def program (many command))

抛开可读性问题不谈,我的问题是:我怎样才能达到相同水平的 我在 Scala 中使用 Clojure 的代码重用?我怎样才能把这个设计模块化 足以让我只能更改或添加必要的规则来获得新的方言 的语言?现在,我能想到的一切——不涉及 用继承实现一个对象系统——就是复制整个 语法。

Clojure 是否有任何资源可以促进这一点?

谢谢。

【问题讨论】:

  • 我很久以前用过 Closure 但我认为deftype/defrecordreify 是你要找的,见the docs,或者它可能是protocols跨度>

标签: scala parsing inheritance clojure polymorphism


【解决方案1】:

这听起来很像可以使用地图实现的东西。基本语法NandCircParsers可以是

(def NandCircParsers
  {:program program
   :block block
   :command command
   :statement statement
   ... ...})

从这个语法中,我们可以创建一个扩展语法NandCircIfParsers,它使用merge 继承自NandCircParsers。可能是这样的:

(def NandCircIfParsers
  (let [ifSttmt ...]
    (merge NandCircParsers
           {:statement (choice (:assign NandCircParsers) ifSttmt)
            :ifSttmt ifSttmt})))

【讨论】:

    猜你喜欢
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-25
    • 2010-12-11
    • 2023-03-28
    相关资源
    最近更新 更多