use*_*113 0 lisp loops functional-programming clojure
如何将这个有效的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.
Run Code Online (Sandbox Code Playgroud)
在我们深入研究代码的Clojure之前,您应该花一些时间来清理Common Lisp代码.使用setq你正在做的方式被认为是最好的样式,并且可能导致最坏的结果:setq意图为变量赋值,但是你的变量list_occur_prob并entropy没有被定义(通过defvar).此外,这段代码看起来像是在分配全局变量(参见参考资料defvar),它们是动态变量,按惯例应该用耳罩标记,例如*entropy*.
但是,对于这一小段代码,你也可以使用let像这样引入的本地非动态变量(警告,我没有任何CL或Clojure环境):
(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)))))
Run Code Online (Sandbox Code Playgroud)
有一些方法可以将apply子句优化到循环中:
(let ((list-occur-prob '(1/2 1/3 1/6)))
(- (loop for i in list-occur-prob
sum (* (log i 2) i))))
Run Code Online (Sandbox Code Playgroud)
现在,Daniel Neal已经向您展示了一个基于map/reduce的解决方案,这里有一个更接近原始循环结构的解决方案,使用递归方法:
(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))
Run Code Online (Sandbox Code Playgroud)
我们正在使用conj而不是collect将结果收集到累加器中.到呼叫ent-helper,基本上是触发的所有值probs通过recur递归调用,取其中值建立迄今收集的(初始为空)第二个参数.如果我们已经用尽所有概率,我们只需返回收集的值.
再次,总结到目前为止的值可以优化到循环中,而不是映射值.