如何将Common Lisp代码的循环部分转换为Clojure?......功能定位

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)

sch*_*eho 5

在我们深入研究代码的Clojure之前,您应该花一些时间来清理Common Lisp代码.使用setq你正在做的方式被认为是最好的样式,并且可能导致最坏的结果:setq意图为变量赋值,但是你的变量list_occur_probentropy没有被定义(通过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递归调用,取其中值建立迄今收集的(初始为空)第二个参数.如果我们已经用尽所有概率,我们只需返回收集的值.

再次,总结到目前为止的值可以优化到循环中,而不是映射值.