如何将这个有效的Common Lisp(SBCL v.1.2.3)代码的循环部分翻译成Clojure(v.1.6)?经过几个小时/几天没有结果,我有点沮丧。在某些地方,我认为我没有得到这种功能定位...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Unconditional Entropy
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Probabilities
(setq list_occur_prob '()) ;; init
;; set probabilities for which we want to calculate the entropy
(setq list_occur_prob '(1/2 1/3 1/6)) ;;
;; Function to calculate the unconditional
;; entropy H = -sigma i=0,n (pi*log2(pi)
;; bits persymbol.
(setq entropy 0) ;; init
(setq entropy (loop for i in list_occur_prob
for y = (* (log_base2 i) i)
collect y
))
(setq entropy (* -1 (apply '+ entropy))) ;; change the sign
;; Print the unconditional entropy in bits per symbol.
(print entropy) ;; BTW, here the entropy is 1.4591479 bits per symbol.
答案 0 :(得分:5)
在深入研究代码的Clojure之前,您应该花一些时间来清理Common Lisp代码。使用setq
你的方式最好被认为是糟糕的风格,并且可能导致最坏的后果:setq
旨在为变量赋值,但你的变量{{1} }和list_occur_prob
未定义(通过entropy
)。此外,这段代码看起来像是你要分配全局变量(再次参见defvar
),这些变量是动态变量,按照惯例,它应该用耳罩标记,例如: defvar
然而,对于这一小段代码,你也可以使用像这样通过*entropy*.
引入的本地非动态变量(警告,我没有任何CL或Clojure环境方便):
let
有很多方法可以将 (let ((list_occur_prob '(1/2 1/3 1/6)))
(loop for i in list_occur_prob
for y = (* (log_base 2 i) i)
collect y into acc
finally (return (* -1 (apply '+ acc)))))
子句优化到循环中:
apply
现在,Daniel Neal已经向您展示了一个基于map / reduce的解决方案,这里使用递归方法更接近原始循环结构:
(let ((list-occur-prob '(1/2 1/3 1/6)))
(- (loop for i in list-occur-prob
sum (* (log i 2) i))))
我们正在使用 (defn ent-helper [probs acc]
(if (seq probs)
(recur (rest probs)
(conj acc (* (log_base 2 (first probs)) (first probs))))
acc))
(let [probs 1/2 1/3 1/6
acc (ent-helper probs [])]
(* -1 (apply + acc))
代替conj
将结果收集到累加器中。通过collect
递归调用实际上为ent-helper
的所有值触发的probs
调用采用(最初为空)第二个参数,其中到目前为止构建的值被收集。如果我们已经耗尽了所有概率,我们只需返回收集的值。
同样,总结到目前为止的值可以优化到循环中,而不是映射到值。
答案 1 :(得分:4)
我将从更多功能 Common Lisp代码开始:
(- (reduce #'+
'(1/2 1/3 1/6)
:key (lambda (i)
(* (log i 2) i))))
你可以在Lisp中编写命令式代码,有很多操作设置变量值,但它不是最好的风格。
即使紧张的LOOP
看起来也不错:
(- (loop for i in '(1/2 1/3 1/6)
sum (* (log i 2) i)))
答案 2 :(得分:3)
他们需要的键操作是map
,它使用函数转换序列。
在您给出的熵示例中,以下内容应该有效:
(def probabilities [1/2 1/3 1/6])
(defn log [base x]
(/ (Math/log x) (Math/log base)))
(defn entropy [probabilities]
(->> probabilities
(map #(* (log 2 %) %)) ; note - #(f %) is shorthand for (fn [x] (f x))
(reduce +)
(-)))
(entropy probabilities) ; => 1.459
使用集合时,经常使用管道运算符(->>
)
清楚地显示一系列操作。我个人觉得它比嵌套的括号语法更容易阅读,特别是如果有很多操作。
在这里,我们首先在序列上映射pi * log2(pi)
函数,
然后使用(reduce +)
答案 3 :(得分:1)
我支持schaueho's answer的一般风格,但如果你愿意,你可以更接近"感觉"使用Clojure for
宏的循环方法:
(apply - 0
(for [prob [1/2 1/3 1/6]]
(* (log prob 2) prob)))
我发现这比schaueho的手动递归版本更容易阅读,而且它的表现要好得多,因为它没有两次遍历列表,也没有将结果累积到临时矢量,等等。
请注意(- (apply + xs))
与(apply - 0 xs)
相同,但您发现哪一个更清楚可能是品味问题。另外,我假设您已经在其他地方定义了合适的log
函数。