鉴于列表,我如何一次处理 N 项目? Ruby在Enumerable
上有each_slice
方法来执行此操作;什么是Lisp等价物?
答案 0 :(得分:5)
Common Lisp' s loop
可以很好地用于此,如以下两个示例所示。第一个示例在列表中循环(x y z)
。但是,默认步骤为cdr
(rest
),因此如果列表为(1 2 3 4 5)
,则会为{{(1 2 3)
,(2 3 4)
等提供(x y z)
1}}。
CL-USER> (loop for (x y z) on '(1 2 3 4 5 6 7 8 9 10 11 12)
do (print (list z y x)))
(3 2 1)
(4 3 2)
(5 4 3)
(6 5 4)
(7 6 5)
(8 7 6)
(9 8 7)
(10 9 8)
(11 10 9)
(12 11 10)
(NIL 12 11)
(NIL NIL 12)
NIL
如果您不希望迭代之间重叠,请将步进功能指定为在列表中向下移动的内容。例如,如果您一次拉三个元素,请使用cdddr
:
CL-USER> (loop for (x y z) on '(1 2 3 4 5 6 7 8 9 10 11 12) by 'cdddr
do (print (list z y x)))
(3 2 1)
(6 5 4)
(9 8 7)
(12 11 10)
NIL
另一个答案使用辅助功能实现了each_slice
的对应部分。但是,请注意partition
(在这个意义上)只有each_slice
的身份函数。这表明我们应该能够使用上面的习语来实现它。匿名函数
(lambda (list)
(nthcdr n list))
是我们需要的步进功能。由于我们不知道细胞在运行时有多少元素,我们不能像上面那样用(x y z)
绑定每个元素。当我们下台并提取子序列 n 元素时,我们必须匹配列表的每个尾部。这是基于loop
的{{1}}实现。
partition
答案 1 :(得分:3)
(defun partition-helper (lst acc x)
(if (< (length lst) x)
acc
(partition-helper (subseq lst x) (cons (subseq lst 0 x) acc) x)))
(defun partition (lst x)
(reverse (partition-helper lst '() x)))
然后你可以:
[25]> (PARTITION '(1 2 3 4 5 6) 2)
((1 2) (3 4) (5 6))
或:
[26]> (PARTITION '(1 2 3 4 5 6) 3)
((1 2 3) (4 5 6))
然后只需mapcar
列表就可以一次处理2个或3个元素。
答案 2 :(得分:2)
如果您想在谓词上拆分列表(而不是固定长度的子列表),我建议nsplit-list
。
对于固定长度的子列表,您可能需要使用loop
:
(defun by-N (list n fun)
(loop for tail on list by (lambda (l) (nthcdr n l))
do (funcall fun (subseq tail 0 (min (length tail) n)))))
(by-n (loop for i from 0 to 20 collect i) 5 #'print)
(0 1 2 3 4)
(5 6 7 8 9)
(10 11 12 13 14)
(15 16 17 18 19)
(20)
请注意,这不是非常有效(它会扫描列表而不是必要的,并分配一个新的子列表以传递给fun
)。
高效版本更复杂:
(defun batch-map (list batch-size function)
"Call FUNCTION on sublists of LIST of size BATCH-SIZE.
Returns the list of return values of FUNCTION."
(do ((tail list (cdr end)) end ret (bs1 (1- batch-size)))
((endp tail) (nreverse ret))
(setq end (nthcdr bs1 tail))
(if (consp end)
(let ((next (cdr end)))
(setf (cdr end) nil)
(unwind-protect (push (funcall function tail) ret)
(setf (cdr end) next)))
(push (funcall function tail) ret))))
答案 3 :(得分:0)
所有答案都是实用且可以使用的,但我相信没有一个可以完全复制Ruby的行为:
> 1.upto(7).each_slice(3) { |x, y, z| p [x, y, z] }
[1, 2, 3]
[4, 5, 6]
[7, nil, nil]
要模拟Ruby,我相信正确的代码类似于:
CL-USER> (defun each-slice (n list thunk)
(apply thunk (loop for i below n collect (nth i list)))
(if (> (length list) n)
(each-slice n (subseq list n) thunk)))
在调用时产生类似的响应:
CL-USER> (each-slice 3 '(1 2 3 4 5 6 7) (lambda (x y z) (print (list x y z))))
(1 2 3)
(4 5 6)
(7 NIL NIL)
NIL