将空列表传递给函数以收集结果

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 (&lt;= 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) 会返回一个新列表,而不更改前一个列表。这个调用应该放在其他一些函数调用中,比如recurlet。否则,你就白费了。

【讨论】:

以上是关于将空列表传递给函数以收集结果的主要内容,如果未能解决你的问题,请参考以下文章

将空数组作为可选参数的默认值传递[重复]

将列表传递给函数以充当多个参数

将空值传递给 SVG 路径(使用 d3.js)以抑制丢失数据

将参数列表传递给Python函数[重复]

Flutter firebase setData()函数在完成异步函数之前将空值分配给字段

将 void 函数的结果传递给 double 函数