Common lisp 在Lisp中一次处理列表中的n个项目

Common lisp 在Lisp中一次处理列表中的n个项目,common-lisp,Common Lisp,给定一个列表,如何一次处理N个项目?Ruby在可枚举的上有这样做的方法;Lisp的等价物是什么 (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 '

给定一个列表,如何一次处理N个项目?Ruby在
可枚举的
上有这样做的方法;Lisp的等价物是什么

(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)))
或:

然后只需在列表上执行
mapcar
,即可一次处理2或3个元素。

Common Lisp可以很好地用于此,如下两个示例所示。第一个示例在列表中循环
(x y z)
。但是,默认的步骤是
cdr
rest
),因此如果列表是
(12345)
,那么对于
(xyz)
,您将得到
(1233)
(234)
,等等

如果不希望迭代之间的重叠,请将步进函数指定为在列表中向下移动得更远的函数。例如,如果一次提取三个元素,请使用
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
用这种技术实现分区 另一个答案是使用辅助函数实现每个_切片的对应项。但是,请注意,
partition
(从这个意义上讲)只是带有标识函数的
每个片。这表明我们应该能够使用上面的习惯用法来实现它。匿名函数

(lambda (list)
  (nthcdr n list))
是我们需要的阶跃函数。因为在运行时之前我们不知道单元有多少个元素,所以我们不能像上面那样用
(x y z)
绑定每个元素。我们确实需要在下一步提取子序列n元素时匹配列表的每个尾部。下面是基于
分区
循环
实现

CL-USER> (defun partition (list cell-size)
           (loop for cell on list by #'(lambda (list)
                                         (nthcdr cell-size list))
              collecting (subseq cell 0 cell-size)))
PARTITION

CL-USER> (partition '(1 2 3 4 5 6) 2)
((1 2) (3 4) (5 6))

如果您想在谓词上拆分列表(而不是固定长度的子列表),我建议您这样做

对于固定长度的子列表,您可能需要使用:

注意,这不是很有效(它扫描列表的次数超过了需要,并分配了一个新的子列表以传递给
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))))

所有答案都是实用的,可以使用,但我相信没有一个能完全复制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

CL没有任何内置的功能来实现这一点,但是您可以在循环中使用SUBSEQ轻松地构建它。在循环中使用SUBSEQ可能会分配大量的中间存储。宏通过
(循环for…on…by…
)本机支持此操作。谢谢大家的回答!谢谢约书亚!使用第二个代码段;需要2个ELT,因此使用cddr。爱的口齿不清@很高兴听到这个消息!Common Lisp在标准中包含了大量的功能,因此浏览小节的和子小节不是一个坏主意,不是要学习所有细节,而是要了解其中的内容,并且知道当您想要某样东西时在哪里查找。例如,我必须检查
(loop…on…by…
语法以确保它是正确的,但我知道应该在哪里查找。公认答案的方法已经做到了:
(loop For(x y z)on'(1 2 3 4)by'cdddr do(print(list x y z))
打印
(1 2 3)
(4 NIL NIL)
并返回
NIL
。是的,您是对的,它可以添加额外的NIL值,但需要您在额外的lambda中使用cd*dr或nthcdr调整“by”参数。Ruby的每个_片段还有另一个行为,循环版本会更好地模拟它。。。在Ruby中,如果在没有给定块的情况下调用每个_片,它将返回一个枚举器实例,该实例可以被链接,使用循环和收集结果可以更好地模拟它。
(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))))
> 1.upto(7).each_slice(3) { |x, y, z| p [x, y, z] }
[1, 2, 3]
[4, 5, 6]
[7, nil, nil]
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