Scheme(和CL)具有点对,其中cons
单元的两个元素都被明确指定(例如(1 . 2)
)而不是隐式指定(例如(1 2)
,其被读作{{ 1}})。
我遇到了这个puzzle,其中点对用于模式匹配,以捕获匹配对象中列表的尾部,例如:
(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))
与模式中的任何内容都不匹配。
我在Clojure中实现了这个难题,我将所有JRM的测试用例从虚线对中传递出来。我正在试图找出如何支持点对模式。
这是我目前的解决方案:
baz
那么如何在没有点对的情况下支持与Clojure中其余对象匹配的模式?
答案 0 :(得分:6)
(编辑:添加了稍微长一点但更清晰的匹配器impl +演示。原始版本仍然低于水平线。)
一种解决方案是引入不同的符号来表示要与seq的尾部匹配的变量,或“点后的变量”。另一种方法是将&
保留为模式中的特殊符号,要求它只能跟随单个模式变量,以与表达式/对象的其余部分匹配,该变量必须是seq。我将探讨下面的第一种方法。
在这里,我冒昧地更改了符号,以便~foo
经常出现变量foo
,而~@foo
是尾部出现。 (可以允许~@
- 匹配子序列,可能匹配序列的最小初始片段(如果有的话),这样剩余部分可以与模式的其余部分匹配;我只是说这是不合适的但是,这个答案的范围。; - ))
请注意,这些实际上是同一个变量的不同出现 - 即仍然只有一个变量类型 - 因为~
引起的绑定之间没有区别 - {{1}引起的出现和绑定}} - 事件
另请注意,您链接到的帖子中的示例不会测试重新绑定以前绑定的变量的尝试(例如,在原始语法中尝试~@
,(pmatch '(~x ~x) '(foo bar))
)。在这种情况下,下面的代码会返回(pmatch '((? x) (? x)) '(foo bar))
,就像因其他原因导致匹配失败时一样。
首先,演示:
nil
这是匹配器:
user> (pmatch '(foo ~pvar1 ~pvar2 bar) '(foo 33 (xyzzy false) bar))
{pvar2 (xyzzy false), pvar1 33}
user> (pmatch '(~av ~@sv) '(foo bar baz))
{sv (bar baz), av foo}
user> (pmatch '(foo ~pvar1 ~pvar2 bar) '(foo 33 false bar))
{pvar2 false, pvar1 33}
user> (pmatch '(foo ~pvar bar) '(quux 33 bar))
nil
user> (pmatch '(a ~var1 (nested (c ~var2))) '(a b (nested (c d))))
{var2 d, var1 b}
user> (pmatch '(a b c) '(a b c))
{}
user> (pmatch '(foo ~pvar1 ~pvar2 bar) '(foo 33 (xyzzy false) bar))
{pvar2 (xyzzy false), pvar1 33}
user> (pmatch '(foo ~@pvar) '(foo bar baz))
{pvar (bar baz)}
user> (pmatch '(~? quux) '(foo quux))
{? foo}
user> (pmatch '~? '(foo quux))
{? (foo quux)}
user> (pmatch '(? ? ?) '(foo quux))
nil
这是最初的单片版本:
(defn var-type [pat]
(when (seq? pat)
(condp = (first pat)
'clojure.core/unquote :atomic
'clojure.core/unquote-splicing :sequential
nil)))
(defn var-name [v]
(when (var-type v)
(second v)))
(defmulti pmatch*
(fn [pat expr bs]
(cond
(= :atomic (var-type pat)) :atom
(= :sequential (var-type pat)) nil
(and (seq? pat) (seq? expr)) :walk
(not (or (seq? pat) (seq? expr))) :exact
:else nil)))
(defmethod pmatch* :exact [pat expr bs]
(when (= pat expr) bs))
(defmethod pmatch* :atom [v expr bs]
(if-let [[_ x] (find bs (var-name v))]
(when (= x expr) bs)
(assoc bs (var-name v) expr)))
(defmethod pmatch* :walk [pat expr bs]
(if-let [[p] pat]
(if (= :sequential (var-type p))
(when (and (seq? expr) (not (next pat)))
(if-let [[_ xs] (find bs (var-name p))]
(when (= xs expr) bs)
(assoc bs (var-name p) expr)))
(when-let [[x] expr]
(when-let [m (pmatch* p x bs)]
(pmatch* (next pat) (next expr) m))))))
(defmethod pmatch* nil [& _] nil)
(defn pmatch
([pat expr] (pmatch pat expr {}))
([pat expr bs] (pmatch* pat expr bs)))