David Touretzky的Common Lisp书籍的练习6.36要求一个函数swap-first-last
,该函数交换任何列表的第一个和最后一个参数。我现在真的很蠢,但是我无法用解决destructuring-bind
。
如何first, *rest, last = (1,2,3,4)
在Common Lisp / with中使用Python做(可迭代解包)操作destructuring-bind
?
经过全部尝试,并通过@WillNess发表了一些评论(谢谢!),我想到了这个主意:
bind
这个想法是尝试细分列表,并使用&rest
中的lambda列表的功能destructuring-bind
,但是使用较短的.
符号-以及usingbutlast
和car
-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的绑定方式不同:lambda-list仅将其余部分作为&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)))
但是,当然,你对此不满意,理论上你可以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
为了向你展示,常见的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
列表中,你可以在宏体进一步处理,在代码中希望促成更多可读性的一部分。
太棒了,感谢您的宏!<3
@upgrd欢迎您!在您的轻快之旅中玩得开心!:)
(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))