【问题标题】:Common Lisp: Destructure a list in first, rest, last (like Python iterable unpacking)Common Lisp:在 first、rest、last 中解构列表(如 Python 可迭代解包)
【发布时间】:2020-12-09 18:45:43
【问题描述】:

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

如何在 Python 中执行 first, *rest, last = (1,2,3,4)(可迭代解包)在 Common Lisp 中/使用 destructuring-bind?

【问题讨论】:

    标签: lisp common-lisp cl


    【解决方案1】:

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

    宏bind

    这个想法是尝试细分列表并在destructuring-bind 中使用lambda 列表的&rest 功能,但是,使用较短的. 表示法-并使用butlast 和car-@987654328 @组合。

    (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 的绑定方式不同:lambda 列表仅将其余部分作为 &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、*rest 和last 部分指定任何名称,您可以在宏的主体中进一步处理它们, 希望有助于提高代码的可读性。

    【讨论】:

    • @upgrd 欢迎!在您的 lisp 之旅中玩得开心! :)
    • (bind (([a, ...bs, c] a_list)) __body__) 会很清楚,我想。我的意思是 [a, ...bs, c] 从字面上看,作为一种模式。 (或其他等价物)。
    • @WillNess - 我添加了进一步的宏定义。由于. 被保留,...rest 不起作用。所以我使用了_rest。这是 clojure 符号吗?
    • 在 clojure 中,可以像这样解构 (let [[[a & b] c] [(butlast [1 2 3 4]) (last [1 2 3 4])]] (list a b c))
    • 我认为是 JS,但我不确定。我首先 dreamt it up 本人,作为 [a, ...bs..., c, ...ds...] 等。然后在 SO,IIRC 上取消了尾随点/看到 JS sn-ps。
    猜你喜欢
    • 1970-01-01
    • 2020-12-03
    • 2011-08-26
    • 2011-05-12
    • 1970-01-01
    • 2012-10-19
    • 2012-10-07
    • 1970-01-01
    • 2021-09-03
    相关资源
    最近更新 更多