Exercise 6.36 of David Touretzky's Common Lisp book asks for a function swap-first-last
that swaps the first and last argument of any list. I feel really stupid right now, but I am unable to solve this with destructuring-bind
.
How can I do what in Python would be first, *rest, last = (1,2,3,4)
(iterable unpacking) in Common Lisp/with destructuring-bind
?
After all trying out, and with some comments by @WillNess (thanks!) I came up with this idea:
bind
The idea is trying to subdivide the list and use the &rest
functionality of the lambda list in destructuring-bind
, however, using the shorter .
notation - and using butlast
and the car
-last
combination.
(defmacro bind ((first _rest last) expr &body body)
`(destructuring-bind ((,first . ,_rest) ,last)
`(,,(butlast expr) ,,(car (last expr)))
,@body)))
usage:
(bind (f _rest l) (list 1 2 3 4)
(list f _rest l))
;; => (1 (2 3) 4)
There is no so elegant possibility like for Python.
destructuring-bind
cannot bind more differently than lambda can: lambda-lists take only the entire rest as &rest <name-for-rest>
.
No way there to take the last element out directly.
(Of course, no way, except you write a macro extra for this kind of problems).
(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)))
But of course, you are in lisp, you could theoretically write macros to
destructuring-bind
in a more sophisticated way ...
But then, destructuring-bind
does not lead to much more clarity than:
(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
To show you, how quickly in common lisp such a macro is generated:
;; 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))))
Usage:
;; 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)
This macro allows you to give any name for the first
, *rest
and last
part of the list, which you can process further in the body of the macro,
hopefully contributing to more readability in your code.
awesome, thanks for the macro! <3
@upgrd welcome! Have fun in your lisp journey! :)
(bind (([a, ...bs, c] a_list)) __body__)
would be quite clear, I think. I do mean[a, ...bs, c]
literally, as a pattern. (or some equivalent).@WillNess - I added a further macro definition. Since
.
is reserved...rest
doesn't work. So I used_rest
. Is this clojure notation?In clojure, one could destructure like this
(let [[[a & b] c] [(butlast [1 2 3 4]) (last [1 2 3 4])]] (list a b c))