我一直无法找到关于我想要了解的内容的任何教义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)无法强制转换为clojure.lang.IFn
答案 0 :(得分:2)
您当然可以将空列表作为结果的起点传递。这里适用的clojure成语是函数调用
(conj list i)
返回一个添加了i的新列表,但它不会改变列表,因此在t函数中,该行不会产生任何结果。要使用该函数调用的结果,它应作为参数传递给recur,类似于(inc i)
要注意的第二件事是if语句中没有其他条件,所以一旦i的值大于10,if语句将返回nil
(defn t1 [list i]
(if (<= i 10)
(recur (conj list i) (inc i))
list))
答案 1 :(得分:1)
这是Clojure中的惯用语吗?
不是。
在Clojure中,data structures are immutable。因此,一旦调用函数传递列表,该函数就无法修改它。所以,它不仅不是惯用的,而且是不可能的。
相反,在Clojure中实现所需的惯用方法是调用递归函数来构建一个较小的列表,并附加到其中。
var my_obj, target_value;
for (let obj_in_arr of my_arr) {
if (obj_in_arr.property1 === target_value) {
my_obj = obj_in_arr;
break;
}
}
话虽如此,你提供的Java片段也不是很好;总的来说,这是一个很好的惯例:
为方法提供返回类型和同时修改参数会导致代码混乱。您的代码的用户无法知道是使用退货还是提供自己要修改的列表。
答案 2 :(得分:0)
请注意,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
内。否则,你徒劳无功。