我写了这个功能(比说明更容易显示):
(split 2 (list 1 2 3 4 5 6))
=> ((1 2) (2 3) (3 4) (4 5) (5 6))
(defn split [n xs]
(if (> (count xs) (dec n))
(cons (take n xs) (split n (rest xs)))
'()))
据我所知,在Clojure中,列表并不是唯一的第一类数据结构。编写这种数据结构不可知是否有意义?无论如何,我的实施是最有效的,如果不是,我将如何使其更有效和/或惯用?
谢谢!
答案 0 :(得分:21)
您可以使用内置分区功能
(partition 2 1 (list 1 2 3 4 5 6))
=> ((1 2) (2 3) (3 4) (4 5) (5 6))
适用于任何序列。
clojure.core/partition
([n coll] [n step coll] [n step pad coll])
Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
do not overlap. If a pad collection is supplied, use its elements as
necessary to complete last partition upto n items. In case there are
not enough padding elements, return a partition with less than n items.
答案 1 :(得分:5)
无需编写自己的实现。 Clojure提供分区, lazy 。如果仅使用数字文字:
,也无需使用列表 (partition 2 '(1 2 3 4 5 6))
答案 2 :(得分:5)
您可以从您的版本中创建一个延迟序列:
(defn split [n xs]
(lazy-seq
(let [m (take n xs)]
(if (= n (count m))
(cons m (split n (rest xs)))))))
(条件与你的'(if(>(count xs)(dec n))'不同的原因是因为它更有效地计算XS中的M个元素而不是每次都计算整个XS集合(这是一种反对懒惰,因为我们不想走整个集合)
想象一下,每次迭代计算怪异范围内的元素会是什么样子:)
(take 10 (split 2 (range 100000000000)))
=> ((0 1) (1 2) (2 3)...)
答案 3 :(得分:2)
我一直在使用Clojure大约一个月,所以我可能没有资格指定最惯用的方式;)
但是你的实现很简短(忽略它也复制了已经提到的内置函数partition)。
该实现已经公平地与数据结构无关 - 因为它使用sequence操作,它适用于所有标准数据结构:
(split 2 [1 2 3 4 5 6])
=> ((1 2) (2 3) (3 4) (4 5) (5 6))
(split 2 #{1 2 3 4 5 6})
=> ((1 2) (2 3) (3 4) (4 5) (5 6))
(split 2 {1 :a 2 :b 3 :c 4 :d})
=> (([1 :a] [2 :b]) ([2 :b] [3 :c]) ([3 :c] [4 :d]))
(split 2 "abcd")
=> ((\a \b) (\b \c) (\c \d))
使用普通递归的主要限制是您受到堆栈大小的限制:
(split 2 (range 10000))
=> java.lang.StackOverflowError
因此,如果您希望输入大小远远超过1k,那么最好使用不使用堆栈的loop / recur:
(defn split-loop [n coll]
(loop [elms coll res [] ]
(if (< (count elms) n)
res
(recur (next elms) (conj res (take n elms))))))