Clojure 中的嵌套映射语句无法正确评估,似乎与 LazySeq 有关
Nested map statements in Clojure don't evaluate properly, seems related to LazySeq
我在 Clojure 中编写了一个函数,它应该采用逻辑表达式和 return 等效表达式,其中所有 not
语句直接作用于变量,如下所示:
(not (and p q r))
变成
(or (not p) (not q) (not r))
它使用德摩根定律将 not
向内推,如果 not
直接作用于另一个 not
语句,它们将抵消。代码如下所示:
(defn transform [expr]
(if
(list? expr)
(if
(=
'not
(first expr)
)
(if
(list? (nth expr 1))
(if
(=
'not
(first (nth expr 1))
)
(transform (first (rest (first (rest expr)))))
(if
(=
'and
(first (nth expr 1))
)
(cons
'or
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
)
(if
(=
'or
(first (nth expr 1))
)
(cons
'and
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
)
expr
)
)
)
expr
)
expr
)
expr
)
)
问题出在这部分:
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
第一个 map
语句使用函数 not-ify
(请原谅双关语)基本上在每个语句之前放置一个 not
。那部分有效。但是,输出不适用于 map transform
,尽管 map transform
部分本身有效。让我告诉你:
如果我在 REPL 中写入以下内容:
(def expr '(not (and q (not (or p (and q (not r)))))))
(map
not-ify
(rest (first (rest expr)))
)
我得到输出((not q) (not (not (or p (and q (not r))))))
如果我然后获取该输出和 运行 (map transform '((not q) (not (not (or p (and q (not r)))))))
,我将得到输出 ((not q) (or p (and q (not r))))
。到目前为止一切顺利。
然而,如果我 运行 一次全部完成,就像这样:
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
我得到的是这个输出:((not q) (not (not (or p (and q (not r))))))
.
如果运行
(def test1
(map
not-ify
(rest (first (rest expr)))
)
)
(map transform test1)
我也得到((not q) (not (not (or p (and q (not r))))))
.
但是如果我运行
(def test2 '((not q) (not (not (or p (and q (not r)))))))
(map transform test2)
我再次得到正确的结果:((not q) (or p (and q (not r))))
。
我的猜测是,这与具有类型 LazySeq
的 map not-ify
输出 (test1
) 有某种关系,而如果我手动输入 (test2
) 它变成了 PersistentList
。我已经尝试在 test1
上 运行ning (into (list))
将其转换为 PersistentList
,以及 doRun
和 doAll
,但没有结果.我能以某种方式停止我的 map not-ify
语句从 returning a LazySeq
?
简短的回答是使用 seq?
而不是 list?
以下是我的实现方式:
(defn push-not-down [expr]
(if (and (seq? expr) (seq? (second expr)))
(let [[outer-op & [[inner-op & inner-args] :as outer-args] :as expr] expr]
(if (= 'not outer-op)
(condp = inner-op
'and (cons 'or (map #(push-not-down (list 'not %)) inner-args))
'or (cons 'and (map #(push-not-down (list 'not %)) inner-args))
'not (first inner-args)
expr)
(if (#{'or 'and} outer-op)
(cons outer-op (map push-not-down outer-args))
expr)))
expr))
(deftest push-not-down-test
(testing "Not or and not and are transformed to and not and or not"
(is (= '(or (not :a) (not :b))
(push-not-down
'(not (and :a :b)))))
(is (= '(and (not :a) (not :b))
(push-not-down
'(not (or :a :b))))))
(testing "Double nots cancel"
(is (= :a
(push-not-down
'(not (not :a))))))
(testing "The rules work together in complex combinations"
(is (= '(and :a (and :b (not :c)))
(push-not-down
'(and (not (not :a)) (not (or (not :b) :c))))))
(is (= '(or (or (and (not :a))))
(push-not-down
'(or (or (and (not :a))))))))
(testing "Nested expressions that don't fit the rules are preserved"
(is (= '(not (inc 1))
(push-not-down
'(not (inc 1)))))
(is (= '(inc (or 2 1))
(push-not-down
'(inc (or 2 1)))))))
对于形式和表达式,列表和序列之间没有显着差异。或者,如果你确实想保持简洁,你只需要更彻底地将序列转换为列表:)
物有所值,...
首先,让我们定义逻辑 inverse
变成什么:
(def opposite {'and 'or, 'or 'and})
(defn inverse [expr]
(let [default (list 'not expr)]
(if (seq? expr)
(let [[op & args] expr]
(if (= op 'not)
(first args)
(cons (opposite op) (map inverse args))))
default)))
让我们测试一下:
(map inverse ['p '(not p) '(and a b) '(and (not a) b)])
;((not p) p (or (not a) (not b)) (or a (not b)))
它短路双重否定以及做 DeMorgan 事情。
现在我们可以表达转换:
(defn transform [expr]
(if (seq? expr)
(let [[op & args] expr]
(if (= op 'not)
(inverse (first args))
(cons op (map transform args))))
expr))
例如,
(transform '(not (and p q r)))
;(or (not p) (not q) (not r))
- 在找到
not
的地方,它 returns 参数的 inverse
。
- 否则,它重构表达式,变换
尽可能使用子表达式,就像
inverse
一样。
如果我们不急于转换子表达式,我们可以简化为
(defn transform [expr]
(or (and (seq? expr)
(let [[op & args] expr]
(if (= op 'not) (inverse (first args)))))
expr))
- 我发现拉出
inverse
操作更容易理解。
- 在简单的
transform
中,我玩过 and
和 or
以及
单臂 if
以避免重复无所事事的情况
下OP的文字。
这与 有什么关系?
- 我无耻地窃取了他关于使用
seq?
而不是
list?
.
- 他对无法识别的操作抛出异常。我的只是让
他们
nil
.
- 我已将
and
和 or
分解为一个案例。
我在 Clojure 中编写了一个函数,它应该采用逻辑表达式和 return 等效表达式,其中所有 not
语句直接作用于变量,如下所示:
(not (and p q r))
变成
(or (not p) (not q) (not r))
它使用德摩根定律将 not
向内推,如果 not
直接作用于另一个 not
语句,它们将抵消。代码如下所示:
(defn transform [expr]
(if
(list? expr)
(if
(=
'not
(first expr)
)
(if
(list? (nth expr 1))
(if
(=
'not
(first (nth expr 1))
)
(transform (first (rest (first (rest expr)))))
(if
(=
'and
(first (nth expr 1))
)
(cons
'or
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
)
(if
(=
'or
(first (nth expr 1))
)
(cons
'and
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
)
expr
)
)
)
expr
)
expr
)
expr
)
)
问题出在这部分:
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
第一个 map
语句使用函数 not-ify
(请原谅双关语)基本上在每个语句之前放置一个 not
。那部分有效。但是,输出不适用于 map transform
,尽管 map transform
部分本身有效。让我告诉你:
如果我在 REPL 中写入以下内容:
(def expr '(not (and q (not (or p (and q (not r)))))))
(map
not-ify
(rest (first (rest expr)))
)
我得到输出((not q) (not (not (or p (and q (not r))))))
如果我然后获取该输出和 运行 (map transform '((not q) (not (not (or p (and q (not r)))))))
,我将得到输出 ((not q) (or p (and q (not r))))
。到目前为止一切顺利。
然而,如果我 运行 一次全部完成,就像这样:
(map
transform
(map
not-ify
(rest (first (rest expr)))
)
)
我得到的是这个输出:((not q) (not (not (or p (and q (not r))))))
.
如果运行
(def test1
(map
not-ify
(rest (first (rest expr)))
)
)
(map transform test1)
我也得到((not q) (not (not (or p (and q (not r))))))
.
但是如果我运行
(def test2 '((not q) (not (not (or p (and q (not r)))))))
(map transform test2)
我再次得到正确的结果:((not q) (or p (and q (not r))))
。
我的猜测是,这与具有类型 LazySeq
的 map not-ify
输出 (test1
) 有某种关系,而如果我手动输入 (test2
) 它变成了 PersistentList
。我已经尝试在 test1
上 运行ning (into (list))
将其转换为 PersistentList
,以及 doRun
和 doAll
,但没有结果.我能以某种方式停止我的 map not-ify
语句从 returning a LazySeq
?
简短的回答是使用 seq?
而不是 list?
以下是我的实现方式:
(defn push-not-down [expr]
(if (and (seq? expr) (seq? (second expr)))
(let [[outer-op & [[inner-op & inner-args] :as outer-args] :as expr] expr]
(if (= 'not outer-op)
(condp = inner-op
'and (cons 'or (map #(push-not-down (list 'not %)) inner-args))
'or (cons 'and (map #(push-not-down (list 'not %)) inner-args))
'not (first inner-args)
expr)
(if (#{'or 'and} outer-op)
(cons outer-op (map push-not-down outer-args))
expr)))
expr))
(deftest push-not-down-test
(testing "Not or and not and are transformed to and not and or not"
(is (= '(or (not :a) (not :b))
(push-not-down
'(not (and :a :b)))))
(is (= '(and (not :a) (not :b))
(push-not-down
'(not (or :a :b))))))
(testing "Double nots cancel"
(is (= :a
(push-not-down
'(not (not :a))))))
(testing "The rules work together in complex combinations"
(is (= '(and :a (and :b (not :c)))
(push-not-down
'(and (not (not :a)) (not (or (not :b) :c))))))
(is (= '(or (or (and (not :a))))
(push-not-down
'(or (or (and (not :a))))))))
(testing "Nested expressions that don't fit the rules are preserved"
(is (= '(not (inc 1))
(push-not-down
'(not (inc 1)))))
(is (= '(inc (or 2 1))
(push-not-down
'(inc (or 2 1)))))))
对于形式和表达式,列表和序列之间没有显着差异。或者,如果你确实想保持简洁,你只需要更彻底地将序列转换为列表:)
物有所值,...
首先,让我们定义逻辑 inverse
变成什么:
(def opposite {'and 'or, 'or 'and})
(defn inverse [expr]
(let [default (list 'not expr)]
(if (seq? expr)
(let [[op & args] expr]
(if (= op 'not)
(first args)
(cons (opposite op) (map inverse args))))
default)))
让我们测试一下:
(map inverse ['p '(not p) '(and a b) '(and (not a) b)])
;((not p) p (or (not a) (not b)) (or a (not b)))
它短路双重否定以及做 DeMorgan 事情。
现在我们可以表达转换:
(defn transform [expr]
(if (seq? expr)
(let [[op & args] expr]
(if (= op 'not)
(inverse (first args))
(cons op (map transform args))))
expr))
例如,
(transform '(not (and p q r)))
;(or (not p) (not q) (not r))
- 在找到
not
的地方,它 returns 参数的inverse
。 - 否则,它重构表达式,变换
尽可能使用子表达式,就像
inverse
一样。
如果我们不急于转换子表达式,我们可以简化为
(defn transform [expr]
(or (and (seq? expr)
(let [[op & args] expr]
(if (= op 'not) (inverse (first args)))))
expr))
- 我发现拉出
inverse
操作更容易理解。 - 在简单的
transform
中,我玩过and
和or
以及 单臂if
以避免重复无所事事的情况 下OP的文字。
这与
- 我无耻地窃取了他关于使用
seq?
而不是list?
. - 他对无法识别的操作抛出异常。我的只是让
他们
nil
. - 我已将
and
和or
分解为一个案例。