我正在尝试在clojure中转换逻辑函数。我希望用户能够输入(convert '(and x y z)
来生成(nor (nor x) (nor y) (nor z)
。因此,我要创建第一个元素为nor
的列表,然后尝试制作通过for
循环时创建的其余元素列表。但是,for
循环仅合并所有列表,并将nor
保留在其外面。我还想知道如何跳过列表中的第一个元素,但是现在这不是我的优先考虑。我对Clojure有点陌生,无法弄清楚如何将所有列表都放回更大的列表中。 not
和or
函数与问题无关。
(defn lookup
"Look up a value, i, in map m and returns the result if it exists.
Otherwise returns i."
[i m]
(get m i i))
(defn makelist
[l]
(for[i[l]] (list 'nor i)))
(defn convert
[l]
(let [p1 (first l)]
(cond
(= p1 'not) (map (fn [i] (lookup i '{not nor})) l)
(= p1 'or) (list 'nor (map(fn [i] (lookup i '{or nor})) l))
(= p1 'and) (list 'nor (makelist l))
:else (print "error"))))
我得到的输出是(nor ((nor (and x y z))))
。我想要的输出是(nor (nor and) (nor x) (nor y) (nor z)
。我也不想要(nor and)
,但直到我想出如何跳过第一个元素,我才希望能够将列表分开。
答案 0 :(得分:2)
我可以看到两个问题:
dotnet update --global dotnet-ef
具有makelist
,因此它只产生一个(for [i [l]] ...)
绑定到整个传入列表i
的单个项目-您在这里想要的是{{ 1}},以便处理l
的每个元素,(for [i l] ...)
的{{1}}子句创建一个包含两个元素的列表:l
和convert
的结果-您想要的是and
这样您将得到一个列表,其中nor
作为第一个元素,然后是调用(makelist l)
的结果的所有元素。我没有检查(cons 'nor (makelist l))
的其他两个部分是否有类似的错误,但是在nor
以上的两个更改中,将产生makelist
答案 1 :(得分:0)
只是为了好玩:我会在脑子上扩展和概括您的任务,以便根据一些规则重写数据结构,因此您可以声明(可能是递归的)重写规则,以将任何输入转换为一般所需的任何输出。 (并练习Clojure)
让我们从简单的转换功能开始:
(defn convert [rules data]
(if-let [res (some (fn [[condition rewrite]]
(when (condition data) (rewrite data)))
rules)]
res
data))
它找到适合您输入的第一条规则(如果有)并应用其转换函数:
(def my-rules [[sequential? (fn [data] (map #(convert my-rules %) data))]
[number? inc]
[keyword? (comp clojure.string/upper-case name)]])
#'user/my-rules
user> (convert my-rules [:hello :guys "i am" 30 [:congratulate :me]])
;;=> ("HELLO" "GUYS" "i am" 31 ("CONGRATULATE" "ME"))
使用这种方法,您的规则将如下所示:
(def rules
[[(every-pred coll? (comp #{'not} first)) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
[(every-pred coll? (comp #{'or} first)) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
[(every-pred coll? (comp #{'and} first)) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
好吧,但是看起来很丑。仍然我们可以消除一些重复,为检查器和变压器引入几个基本功能:
(defn first-is
"returns a function checking that the input is collection and it's head equals to value"
[value]
(every-pred coll? (comp #{value} first)))
将您的规则转换为:
(def rules
[[(first-is 'not) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
[(first-is 'or) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
,然后引入替换重写规则:
(defn replacing
([new] [(constantly true) (constantly new)])
([old new] [#{old} (constantly new)]))
引导我们
(def rules
[[(first-is 'not) (fn [data] (map (partial convert [(replacing 'not 'nor)]) data))]
[(first-is 'or) (fn [data] (map (partial convert [(replacing 'or 'nor)]) data))]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
现在我们可以看到对功能有需求,可以转换集合中的每个项目。让我们对其进行介绍:
(defn convert-each [rules]
(fn [data] (map #(convert rules %) data)))
(def rules
[[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
[(first-is 'or) (convert-each [(replacing 'or 'nor)])]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
user> (convert rules '(or x y z))
;;=> (nor x y z)
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
现在好多了,但最后一个子句仍然很难看。我可以考虑介绍一下使用单独的规则转换头和尾然后转换后的头和尾的函数:
(defn convert-cons [head-rules tail-conversion]
(fn [[h & t]] (cons (convert head-rules h) (tail-conversion t))))
(defn transforming [transformer]
[(constantly true) transformer])
(def rules
[[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
[(first-is 'or) (convert-each [(replacing 'or 'nor)])]
[(first-is 'and) (convert-cons [(replacing 'nor)]
(convert-each [(transforming #(list 'nor %))]))]])
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))