将空列表传递给函数以收集结果
Posted
技术标签:
【中文标题】将空列表传递给函数以收集结果【英文标题】:Passing Empty Lists to Functions to Collect Results 【发布时间】:2018-06-28 03:24:06 【问题描述】:我一直无法找到任何关于我试图理解的内容的教义 Clojure 示例,并希望对此有所了解。
传统上,当我在命令式语言中使用递归时,我经常将一个空列表传递给递归函数,以便通过堆栈收集一些计算的结果。
作为 Java 中的一个小例子:
public List<Integer> results = new ArrayList<>();
private List<Integer> add(List<Integer> list, int count)
if(count > 10)
return list;
count = count + 1;
list.add(count);
return add(list, count);
如果运行:
add(results, 0)
将产生一个从 0 到 10 的值列表。
这是 Clojure 中的惯用语吗?
(defn t [list i]
(conj list i)
(if (<= i 10)
(recur list (inc i))))
(t '() 0)
其次,为什么在我评价的时候:
(t '() 0)
我是否收到此错误:
clojure.lang.Compiler$CompilerException: java.lang.ClassCastException: java.lang.Long (in module: java.base) cannot be cast to clojure.lang.IFn
【问题讨论】:
conj 不会发生变异,因此您需要显式传递其返回值。即(defn t [list i] (if (<= i 10) (recur (conj list i) (inc i)) list))
...至于惯用语,我不是一个clojurist。这种风格在我看来更像方案。
【参考方案1】:
您当然可以传入一个空列表作为结果的起点。这里适用的 clojure 成语是函数调用
(conj list i)
返回一个添加了 i 的新列表,但它不会改变列表,因此在 t 函数中,该行不会产生任何结果。要使用该函数调用的结果,应将其作为参数传递给类似于 (inc i) 的 recur
需要注意的第二点是if语句中没有else条件,所以一旦i的值大于10,if语句就会返回nil
(defn t1 [list i]
(if (<= i 10)
(recur (conj list i) (inc i))
list))
【讨论】:
【参考方案2】:这是 Clojure 中的惯用语吗?
不是。
在 Clojure 中,data structures are immutable。因此,一旦您调用传递列表的函数,该函数就无法修改它。所以,这不仅不是惯用的,而且是不可能的。
相反,在 Clojure 中实现所需功能的惯用方法是调用递归函数来构建一个较小的列表,然后追加到其中。
(defn my-range [n]
(if (< n 0)
[] ; empty list
(conj
(my-range (dec n)) ; recursive call to build smaller list
n)))
话虽如此,您提供的Java的sn-p也不是很好;一般来说,这是一个很好的约定:
如果方法具有 void 返回类型,您可能会认为它会产生副作用,并且可能会修改其参数之一 如果方法有返回类型,那么您可能希望参数不会被修改。给方法一个返回类型和同时修改一个参数会使代码混乱。您的代码的用户不知道是使用返回还是提供自己的要修改的列表。
【讨论】:
【参考方案3】:注意,conj
对于向量和列表类型的行为不同。对于向量,它会在末尾附加一个元素。对于一个列表,它把它放在它的前面:
user=> (conj [1 2 3] 4)
[1 2 3 4]
user=> (conj '(1 2 3) 4)
(4 1 2 3)
因此,如果有人传递向量而不是列表,他或她将面临不可预知的行为。请注意,在 Clojure 中,向量比列表更常见(例如,与 Scheme 或 Common 列表不同)。
concat
函数适用于两种类型:
user=> (concat [1 2 3] [4])
(1 2 3 4)
user=> (concat '(1 2 3) [4])
(1 2 3 4)
然后,调用(conj list i)
会返回一个新列表,而不更改前一个列表。这个调用应该放在其他一些函数调用中,比如recur
或let
。否则,你就白费了。
【讨论】:
以上是关于将空列表传递给函数以收集结果的主要内容,如果未能解决你的问题,请参考以下文章
将空值传递给 SVG 路径(使用 d3.js)以抑制丢失数据