为什么这个函数返回 nil?
Why this function is returning nil?
我正在学习Clojure和函数式编程,为了练习,我正在研究4clojure的问题。
此功能(不是最好的方法.. 我知道)正在运行。
(反向交错)但是,该函数正在重新调整 nil。
(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)
问题是……为什么?
请务必记住,Clojure 并不像大多数命令式语言那样真正执行 语句。使用 do
可以评估多个表达式的副作用,然后 return 最后一个的值,并且 let
和 fn
等几个结构包含隐式 do
。但是除了最后一个表达式之外的任何表达式都不可能停止计算并说 "we're done" 除非它抛出异常。因此,整行
(if (nil? (first coll)) (do (println :ret ret) ret))
只能从它的副作用来看。它将评估为 ret
或 nil
中的值,然后将被丢弃。
然后我们开一个when-let
。因为它是 when
的变体,如果满足条件,它将 return 其主体的值,否则将 nil
。正文是一条recur
到循环开始的指令,所以这个循环只能在值nil
.
处终止
自然的解决方法似乎是采用 if
形式的最后一个括号并将其移动到 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))
我正在学习Clojure和函数式编程,为了练习,我正在研究4clojure的问题。
此功能(不是最好的方法.. 我知道)正在运行。 (反向交错)但是,该函数正在重新调整 nil。
(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)
问题是……为什么?
请务必记住,Clojure 并不像大多数命令式语言那样真正执行 语句。使用 do
可以评估多个表达式的副作用,然后 return 最后一个的值,并且 let
和 fn
等几个结构包含隐式 do
。但是除了最后一个表达式之外的任何表达式都不可能停止计算并说 "we're done" 除非它抛出异常。因此,整行
(if (nil? (first coll)) (do (println :ret ret) ret))
只能从它的副作用来看。它将评估为 ret
或 nil
中的值,然后将被丢弃。
然后我们开一个when-let
。因为它是 when
的变体,如果满足条件,它将 return 其主体的值,否则将 nil
。正文是一条recur
到循环开始的指令,所以这个循环只能在值nil
.
处终止
自然的解决方法似乎是采用 if
形式的最后一个括号并将其移动到 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))