Clojure 中用于模式匹配的点对的类似物
方案(和 CL)具有点对,其中 cons
单元格的两个元素均显式指定(例如 (1 . 2)
),而不是隐式指定(例如 ( 1 2)
读作 (1 . (2 . nil))
)。
我遇到了这个谜题,其中点对用于模式匹配捕获正在匹配的对象中列表的尾部,例如:
(pmatch '(foo . (? pvar)) '(foo bar baz))
;; => ((pvar bar baz))
这里 '(foo . (? pvar))
是一个模式,'(foo bar baz)
是匹配的对象模式。模式中的 foo
是一个文字,而 (? pvar)
是一个模式变量,它匹配 (bar baz)
并绑定符号 pvar
到那场比赛。 pmatch
函数返回模式变量和绑定匹配的关联列表。
如果模式是 '(foo (? pvar))
,则匹配将会失败,因为 baz
不会匹配模式中的任何内容。
我已经在 Clojure 中实现了这个难题,并且通过了除点对测试用例之外的所有 JRM 测试用例。我正在尝试找出如何支持点对模式。
这是我当前的解决方案:
(defn pattern-variable? [pv]
(when (seq? pv)
(let [[qmark var] pv]
(and (= (count pv) 2)
(= qmark '?)
(or (symbol? var)
(keyword? var)))))
(defn pattern-variable [pv]
(second pv))
(defn pmatch
([pat obj] (pmatch pat obj {}))
([pat obj binds]
(cond (not (coll? pat))
(when (= pat obj) binds)
(pattern-variable? pat)
(assoc binds (pattern-variable pat) obj)
(seq? pat) (let [[pat-f & pat-r] pat]
(when (seq? obj)
(when-let [binds (pmatch pat-f (first obj) binds)]
(pmatch pat-r (next obj) binds))))
:else nil)))
那么如何支持与 Clojure 中的对象的其余部分匹配而无需点对的模式呢?
Scheme (and CL) has dotted-pairs where both elements of a cons
cell are specified explicitly (e.g. (1 . 2)
) rather than implicitly (e.g. (1 2)
which is read as (1 . (2 . nil))
).
I came across this puzzle where dotted-pairs are used in pattern matching to capture the tail of a list in the object being matched, e.g.:
(pmatch '(foo . (? pvar)) '(foo bar baz))
;; => ((pvar bar baz))
Here '(foo . (? pvar))
is a pattern and '(foo bar baz)
is the object matched against the pattern. foo
in the pattern is a literal, whereas (? pvar)
is a pattern variable which matches (bar baz)
and binds the symbol pvar
to that match. The pmatch
function returns an association list of pattern variables and bound matches.
Had the pattern been '(foo (? pvar))
, the match would fail, because baz
would not match anything in the pattern.
I've implemented the puzzle in Clojure, and I pass all of JRM's test cases apart from the dotted-pair one. I'm trying to figure out how to possibly support the dotted-pair pattern as well.
Here's my current solution:
(defn pattern-variable? [pv]
(when (seq? pv)
(let [[qmark var] pv]
(and (= (count pv) 2)
(= qmark '?)
(or (symbol? var)
(keyword? var)))))
(defn pattern-variable [pv]
(second pv))
(defn pmatch
([pat obj] (pmatch pat obj {}))
([pat obj binds]
(cond (not (coll? pat))
(when (= pat obj) binds)
(pattern-variable? pat)
(assoc binds (pattern-variable pat) obj)
(seq? pat) (let [[pat-f & pat-r] pat]
(when (seq? obj)
(when-let [binds (pmatch pat-f (first obj) binds)]
(pmatch pat-r (next obj) binds))))
:else nil)))
So how can I support patterns that match the rest of the object in Clojure without dotted-pairs?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
(编辑:添加了一个稍长但明显更清晰的匹配器 impl + 演示。原始版本仍然低于水平规则。)
一种解决方案是引入不同的符号来表示要匹配的变量seq 的尾部,或“点后变量”。另一种方法是将
&
保留为模式中的特殊符号,要求它后面只能跟一个与表达式/对象的其余部分相匹配的单个模式变量,该变量必须是一个序列。我将在下面探讨第一种方法。在这里,我擅自更改了符号,使
~foo
是变量foo
的常规出现,而~@foo
是尾部出现。 (可以允许~@
与子序列匹配,也许匹配序列的最小初始片段(如果有的话),以便剩余部分可以与模式的其余部分匹配;我只想说不过,这超出了这个答案的范围;-))请注意,这些实际上是同一变量的不同出现 - 即仍然只有一种变量类型 - 因为
~
-出现和由~@
出现引起的绑定。另请注意,您链接到的帖子中的示例不会测试重新绑定先前绑定的变量的尝试(例如尝试
(pmatch '(~x ~x) '(foo bar))
,(pmatch '((? x) (? x)) '(foo bar))
在原始语法中)。在这种情况下,下面的代码将返回nil
,就像当匹配由于其他原因失败时一样。首先,一个演示:
这是匹配器:
这是原始的整体版本:
(Edit: Added a slightly longer, but significantly clearer matcher impl + a demo. The original remains below the horizontal rule.)
One solution would be to introduce a different notation to signify a variable to be matched against the tail of a seq, or "variable after dot". Another one would be to reserve
&
as a special symbol in patterns with the requirement that it may only be followed by a single pattern variable to be matched against the rest of the expression / object, which must be a seq. I'll explore the first approach below.Here I took the liberty of changing the notation so that
~foo
is a regular occurrence of the variablefoo
and~@foo
is a tail occurrence. (One could permit~@
-matching against subsequences, perhaps matching the minimal initial fragment of a sequence, if any, such that the remainder can be matched against the rest of the pattern; I'll just say this is out of scope for this answer, though. ;-))Note that these really are different occurrences of the same variable -- i.e. there is still only one variable type -- since no distinction is being made between bindings arising from
~
-occurrences and bindings arising from~@
-occurrences.Also note that the examples in the post you linked to do not test for attempts to rebind a previously bound variable (e.g. try
(pmatch '(~x ~x) '(foo bar))
,(pmatch '((? x) (? x)) '(foo bar))
in the original syntax). The code below returnsnil
in such cases, as it does when the match fails for other reasons.First, a demo:
Here's the matcher:
And here's the original monolithic version: