这是一个 clojure.core.match 错误还是只有我一个?
Is this a clojure.core.match bug or it's just me?
(match
[[1 2 3]]
[(:or [_ _ 2]
[3 _ _])] :a0
[(:or [_ _ 1]
[1 _ _])] :a1
:else :else)
=> :else
在第一个片段中,我预计它会 return :a1
。
奇怪。
这个有效:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _])] :a1
:else :else)
=> :a0
这是预期的行为吗?
我会说这不是因为手册中从未提及您使用 :or
的方式。它应该用在这样的表达式中:
[4 (:or 5 6 7) _] :a1
所以你的代码应该看起来像
(match
[[1 2 3]]
[[_ _ 2]] :a0
[[3 _ _]] :a0
[[_ _ 1]] :a1
[[1 _ _]] :a1
:else :else)
但也许你应该咨询作者。很难说初衷是什么。
我认为这是 specialize-or-pattern-row
中的错误。我相信 groupable?
测试是错误的,因为在你的情况下,它对你的两个 OrPattern
是成功的,所以第二个 OrPattern
被第一个扩展替换(ps
是第一个 OrPattern
) 的子模式。
您可以通过向第二个 :or
添加虚拟模式来解决此问题,这将强制 groupable?
为 return false:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _]
:dummy)] :a1
:else :else)
一个可能更好的 specialize-or-pattern-row
(copy-as
可以通过将 :as
复制到每个 :as
子模式):
(defn copy-as [dest src]
(if-let [as (-> src meta :as)]
(vary-meta dest assoc :as as)
dest))
(defn specialize-or-pattern-row [row pat ps]
(let [p (first row)]
(if (identical? p pat)
(map (fn [p] (update-pattern row 0 (copy-as p pat))) ps)
[row])))
Seems like a bug in core.match. I used a slightly simpler statement that has the same problem.
(match
[[1]]
[(:or [3] [])] :a0
[(:or [1] [])] :a1
:else :else)
This also returns :else. I ran it through macroexpand and extracted the logic. It turns into something like this.
(let
[x [1]]
(cond (and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a0
(and (vector? x) (== (count x) 0)) :a0
(and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a1
(and (vector? x) (== (count x) 0)) :a1))
On the 5th line there you can see the error, it is 3 instead of 1. For some reason it takes the value that is in the first :or pattern instead of the second rows value.
这 patch 似乎解决了问题。
谢谢大家!
P.S.: 不过我还没有测试补丁。
(match
[[1 2 3]]
[(:or [_ _ 2]
[3 _ _])] :a0
[(:or [_ _ 1]
[1 _ _])] :a1
:else :else)
=> :else
在第一个片段中,我预计它会 return :a1
。
奇怪。
这个有效:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _])] :a1
:else :else)
=> :a0
这是预期的行为吗?
我会说这不是因为手册中从未提及您使用 :or
的方式。它应该用在这样的表达式中:
[4 (:or 5 6 7) _] :a1
所以你的代码应该看起来像
(match
[[1 2 3]]
[[_ _ 2]] :a0
[[3 _ _]] :a0
[[_ _ 1]] :a1
[[1 _ _]] :a1
:else :else)
但也许你应该咨询作者。很难说初衷是什么。
我认为这是 specialize-or-pattern-row
中的错误。我相信 groupable?
测试是错误的,因为在你的情况下,它对你的两个 OrPattern
是成功的,所以第二个 OrPattern
被第一个扩展替换(ps
是第一个 OrPattern
) 的子模式。
您可以通过向第二个 :or
添加虚拟模式来解决此问题,这将强制 groupable?
为 return false:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _]
:dummy)] :a1
:else :else)
一个可能更好的 specialize-or-pattern-row
(copy-as
可以通过将 :as
复制到每个 :as
子模式):
(defn copy-as [dest src]
(if-let [as (-> src meta :as)]
(vary-meta dest assoc :as as)
dest))
(defn specialize-or-pattern-row [row pat ps]
(let [p (first row)]
(if (identical? p pat)
(map (fn [p] (update-pattern row 0 (copy-as p pat))) ps)
[row])))
Seems like a bug in core.match. I used a slightly simpler statement that has the same problem.
(match
[[1]]
[(:or [3] [])] :a0
[(:or [1] [])] :a1
:else :else)
This also returns :else. I ran it through macroexpand and extracted the logic. It turns into something like this.
(let
[x [1]]
(cond (and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a0
(and (vector? x) (== (count x) 0)) :a0
(and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a1
(and (vector? x) (== (count x) 0)) :a1))
On the 5th line there you can see the error, it is 3 instead of 1. For some reason it takes the value that is in the first :or pattern instead of the second rows value.
这 patch 似乎解决了问题。
谢谢大家!
P.S.: 不过我还没有测试补丁。