我被困在4Clojure网站的Pascal's Trapezoid,你需要建立一个懒惰的数字序列。
我的第一枪就是:
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(map +
(cons 0 x)
(conj x 0)
)))))
哪个不起作用:
user=> (take 5 (pascal [1 1]))
([1 1] (1 2 1) (0 2 4 2) (0 0 4 8 4) (0 0 0 8 16 8))
然而,以这种方式编写它是有效的:
(defn pascal2 [x]
(cons x
(lazy-seq
(pascal2
(map +
(concat [0] x)
(concat x [0])
)))))
user=> (take 5 (pascal2 [1 1]))
([1 1] (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1))
那么,我到底做错了什么? cons / conj和concat有什么区别?
答案 0 :(得分:3)
正如其他人所说,conj
根据具体的集合类型将其收到的元素插入到不同的位置,请参阅this SO问题以获取有关{{1}之间差异的更多详细信息}和conj
。
在cons
函数的第一个版本中,您提供了一个向量作为初始参数,因此表达式pascal
将在向量的末尾插入(conj x 0)
以进行计算系列中的第二个元素但是,因为map返回一个惰性序列,当计算第三个元素时,插入发生在开头(0
),这导致系列中的元素错误从那以后。
要使用(conj (map inc '(0)) 2) ;= (2 1)
和cons
方法,您必须确保使用conj
而不是mapv
返回向量。
map
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(mapv +
(cons 0 x)
(conj x 0))))))
(take 5 (pascal [1 1]))
;= ([1 1] [1 2 1] [1 3 3 1] [1 4 6 4 1] [1 5 10 10 5 1])
的缺点是它非常渴望它会计算pascal元素中的所有成员,而不是仅仅在实际需要它之前将其保留。
另一方面,当你使用mapv
时,确保你将元素追加到最后,并且所有内容都是懒惰的,但附加信息并不像向量那样便宜,请参阅here了解更多信息信息。
无论这些因素如何,您仍然可以在两种情况下都使用concat
,因为它的作用就是您在任何一种情况下所需要的(即在集合的开头插入一个元素)。
cons
答案 1 :(得分:2)
conj clojure.core
(conj coll x)
(conj coll x & xs)
缀[OIN]。返回xs'已添加'的新集合。 (没法 item)返回(item)。 “添加”可能发生在不同的“地方” 取决于具体类型。
conj
接受第一个参数作为集合,这意味着coll
必须是集合类型。 conj
会将x
添加到coll
中的新集合返回,添加x
的地点取决于coll
的类型。
e.g。
> (conj [1] [0])
[1 [0]] ; See [0] is added into [1] as an element. Instead of returning [1 0], it returns [1 [0]]
> (conj [1] 0)
[1 0]
> (conj '(1) 0)
(0 1) ;See the element `0` position is different in each case.
concat clojure.core
(concat)
(concat x)
(concat x y)
(concat x y & zs)
返回表示元素串联的惰性seq 提供的歌词。
concat
接受所有参数作为集合类型,这与conj
不同。 concat
返回参数的连接。
e.g。
> (concat [0] [1])
(0 1)
> (concat [0] [[1]])
(0 [1])
> (concat [0] 1) ;See the second argument is not a collection type, thus the function throws an exception.
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
>
cons clojure.core
(cons x seq)
返回一个新的seq,其中x是第一个元素,seq是其余元素。
cons
的文件清楚地说明cons
将如何运作。 cons
的第二个参数必须是seq
。
e.g。
> (cons [1] [0])
([1] 0) ; [1] is the first element and (0) is the rest
> (first (cons [1] [0]))
[1]
> (rest (cons [1] [0]))
(0)
> (cons 1 [0]) ; 1 is the first element and (0) is the rest
(1 0)
> (cons [1] 0) ;the second argument is not a seq, throwing exception
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
答案 2 :(得分:2)
conj
上的 list
会将元素添加到列表的前面。
如果将列表转换为矢量,它将起作用。
user=> (conj '(1 2 3) 4)
(4 1 2 3)
user=> (conj [1 2 3] 4)
[1 2 3 4]