为什么这个函数返回nil?

时间:2015-02-21 18:08:34

标签: clojure

我正在学习Clojure和功能编程,为了练习,我正在研究4clojure问题。

这个功能,(不是最好的方法......我知道)正在发挥作用。 (反向交错)但是,该功能重新调整为零。

(defn reverse_interleave
  [coll ss]
  (let [xx (dec ss)]
    (loop [coll (reverse coll) s xx _ss ss ret `()]
      (if (nil? (first coll)) (do (println :ret ret) ret))
      (when-let [x (first coll)]
                        (recur 
                          (rest coll)
                          (if (zero? s) xx (dec s))
                          (if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
                          (if (zero? _ss)
                            (map-indexed #(if (= % s) (cons x %2) %2) ret)
                            (cons (list x) ret))
                            ))
      )) ret)


(reverse_interleave (range 9) 3)

问题是......为什么?

1 个答案:

答案 0 :(得分:2)

重要的是要记住,Clojure并不像大多数命令式语言那样真正做语句。使用do,可以评估副作用的多个表达式,然后返回最后一个的值,而letfn等几个结构包含隐式do。但是除了最后一个表达式之外的任何表达式都不可能停止评估并说“我们已经完成”,除非它抛出异常。就这样,整行

(if (nil? (first coll)) (do (println :ret ret) ret))

只能通过其副作用来看待。它将评估为retnil中的值,然后将被丢弃。

然后我们打开when-let。因为它是when的变体,如果条件满足,它将返回其正文的值,如果不满足则返回nil。正文是recur到循环开头的指令,因此该循环只能以值nil终止。
自然修复似乎是采用if形式的最后一个paren并在when-let之后移动它,以便整个表单是else-case的值。您的代码也无法为我编译,因为它在创建它的循环之外使用名称ret,但随着它的消失它似乎按预期工作:

(defn reverse_interleave
  [coll ss]
  (let [xx (dec ss)]
    (loop [coll (reverse coll) s xx _ss ss ret `()]
      (if (nil? (first coll)) 
        (do (println :ret ret) ret)
        (when-let [x (first coll)]
          (recur 
           (rest coll)
           (if (zero? s) xx (dec s))
           (if (or (= 1 _ss) (zero? _ss)) 0 (dec _ss))
           (if (zero? _ss)
             (map-indexed #(if (= % s) (cons x %2) %2) ret)
             (cons (list x) ret))))))))

(reverse_interleave (range 9) 3)
;;returns ((0 3 6) (1 4 7) (2 5 8))