原始脚本是这样的:
#lang racket
(for ([i (in-range 3)])
(for ([j (in-range 9)])
(display "X"))
(display "\n"))
(for ([i (in-range 6)])
(for ([j (in-range 3)])
(display " "))
(for ([j (in-range 3)])
(display "X"))
(for ([j (in-range 3)])
(display " "))
(display "\n"))
(for ([i (in-range 3)])
(for ([j (in-range 9)])
(display "X"))
(display "\n"))
输出结果为:
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXX
XXX
XXX
XXX
XXX
XXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
我想知道我是否可以使用这样的DSL重写这个:
(define a
"3 9 X
6 3 b 3 X 3 b
3 9 X")
然后:
(interpret a)
绘制此图表。
有谁知道最好的方法是什么?
答案 0 :(得分:8)
要解决此类问题,请首先描述捕获DSL中所需操作的数据类型,而不是专注于表面语法。一旦掌握了数据类型,就可以更轻松地解决问题。
乍一看,我们可以用您的语言设计3种基本形式:
我们可以用原始字符串和结构来表示这个不相交的类。让我们将这个类称为“pexpr”,作为“可打印的expr”。在代码中:
;; An pexpr is one of the following:
;; * a primitive string,
;; * a seq, or
;; * a repeat
(struct seq (bodies) #:transparent) ;; bodies is a list of pexpr
(struct repeat (n body) #:transparent) ;; n is a number, body is a pexpr
将“辅助函数”作为缩写可能会有所帮助,因为“seq”和“repeat”本身有点啰嗦。
;; For convenience, we define some abbreviations s and r for seq and repeat,
;; respectively.
(define (s . bodies)
(seq bodies))
(define (r n . bodies)
(repeat n (seq bodies)))
您的示例“I”字符串可以写成:
(define an-example
(s
(r 3 (r 9 "X") "\n")
(r 6 (r 3 " ") (r 3 "X") "\n")
(r 3 (r 9 "X") "\n")))
请注意,此编码具有新行的显式表示,仅从表面语法中隐含。然后它成为解析器的工作,在表面语法中取代线并将它们变成pexprs,但这不应该太困难。希望。 :)
无论如何,解释函数就变成了填写这样一个模板细节的问题:
(define (interpret pexpr)
(match pexpr
[(? string?)
...]
[(struct seq (bodies))
...]
[(struct repeat (n body))
...]))
其中'...'应该很容易填写。
这种问题的解决方法由How to Design Programs和Programming Languages: Application and Interpretation描述。我建议看看它们:它们是好东西。
答案 1 :(得分:1)
当然,这看起来很可行。这主要是一个解析问题。我会像这样分手。每个输入行都指定一个输出行块。找出一种在Racket中表示的好方法。举一些例子,确保它涵盖了你想要覆盖的内容。接下来,我可能会编写渲染其中一个块的函数。大多数情况下,我先做那个,这样我才能满足于看到输出。然后,我会编写一个函数,列出这些块规范并将它们全部输出。然后,我会编写一个解析单行输入的函数。看起来您可以使用空格分割这些行(例如,使用" regexp-split"),以及 然后使用ad-hoc解析器处理这些列表。这是我认为我最容易出错的部分,在编写之前我会编写一堆测试用例。最后,您需要一个在每行输入上调用此解析器的函数,然后将生成的块规范发送到显示函数。