lisp - Common Lisp : Destructure a list in first, rest,最后(像 Python 可迭代解包)

标签 lisp common-lisp cl

David Touretzky 的 Common Lisp 书的练习 6.36 要求一个函数 swap-first-last 来交换任何列表的第一个和最后一个参数。我现在觉得真的很愚蠢,但我无法用 destructuring-bind 解决这个问题。

我怎样才能在 Python 中做 first, *rest, last = (1,2,3,4) (可迭代解包)在 Common Lisp/with destructuring-bind ?

最佳答案

经过所有尝试,以及@WillNess 的一些评论(谢谢!)我想出了这个主意:

bind

想法是尝试分割列表并使用&rest destructuring-bind 中 lambda 列表的功能, 但是,使用较短的 .符号 - 并使用 butlastcar - last组合。

(defmacro bind ((first _rest last) expr &body body)
`(destructuring-bind ((,first . ,_rest) ,last) 
    `(,,(butlast expr) ,,(car (last expr)))
  ,@body)))

用法:

(bind (f _rest l) (list 1 2 3 4) 
  (list f _rest l))
;; => (1 (2 3) 4)

我的原始答案

没有像 Python 那样优雅的可能性。 destructuring-bind不能比 lambda 更不同地绑定(bind):lambda-lists 只将整个其余部分作为 &rest <name-for-rest> . 没有办法直接取出最后一个元素。 (当然,没办法,除非你为这种问题写一个额外的宏)。

(destructuring-bind (first &rest rest) (list 1 2 3 4)
  (let* ((last (car (last rest)))
         (*rest (butlast rest)))
    (list first *rest last)))
;;=> (1 (2 3) 4)

;; or:
(destructuring-bind (first . rest) (list 1 2 3 4)
  (let* ((last (car (last rest)))
         (*rest (butlast rest)))
   (list first *rest last)))

当然,你是 lisp,理论上你可以编写宏来 destructuring-bind以更复杂的方式......

但是,destructuring-bind不会比以下更清晰:

(defparameter *l* '(1 2 3 4))

(let ((first (car *l*))
      (*rest (butlast (cdr *l*)))
      (last (car (last *l*))))
  (list first *rest last))

;;=> (1 (2 3) 4)

first-*rest-last

为了向您展示,在 common lisp 中生成这样一个宏的速度有多快:

;; first-*rest-last is a macro which destructures list for their 
;; first, middle and last elements.
;; I guess more skilled lisp programmers could write you
;; kind of a more generalized `destructuring-bind` with some extra syntax ;; that can distinguish the middle pieces like `*rest` from `&rest rest`.
;; But I don't know reader macros that well yet.

(ql:quickload :alexandria)

(defmacro first-*rest-last ((first *rest last) expr &body body)
  (let ((rest))
    (alexandria:once-only (rest)
      `(destructuring-bind (,first . ,rest) ,expr
        (destructuring-bind (,last . ,*rest) (nreverse ,rest)
          (let ((,*rest (nreverse ,*rest)))
            ,@body))))))

;; or an easier definition:

(defmacro first-*rest-last ((first *rest last) expr &body body)
  (alexandria:once-only (expr)
    `(let ((,first (car ,expr))
           (,*rest (butlast (cdr ,expr)))
           (,last (car (last ,expr))))
       ,@body))))

用法:

;; you give in the list after `first-*rest-last` the name of the variables
;; which should capture the first, middle and last part of your list-giving expression
;; which you then can use in the body.

(first-*rest-last (a b c) (list 1 2 3 4)
  (list a b c))
;;=> (1 (2 3) 4)

此宏允许您为 first 指定任何名称, *restlast列表的一部分,您可以在宏的主体中进一步处理, 希望有助于提高代码的可读性。

关于lisp - Common Lisp : Destructure a list in first, rest,最后(像 Python 可迭代解包),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65223006/

相关文章:

character-encoding - 如何处理 Common Lisp (SBCL) 中的重音符号?

lisp - 在没有明确提及的情况下永久改变变量?

visual-studio - Visual Studio 脚本未正确设置环境变量

c++ - "CL.exe"未在独立项目中编译 C++

recursion - 使用 LISP 递归查找列表中的元素位置

scope - 我可以在 lisp 中嵌套 defmethod/defgeneric 吗?

lisp - 不确定这条线的作用

common-lisp - 修改属性列表

sorting - 如何使用子列表对列表进行排序(常见的 Lisp)