I am trying to transpose a list of lists; my comments indicate the thought process.
(setq thingie '((1 2 3) (4 5 6) (7 8 9))) ;;test case
(defun trans (mat)
(if (car mat)
(let ((top (mapcar 'car mat)) ;;slice the first row off as a list
(bottom (mapcar 'cdr mat))) ;;take the rest of the rows
(cons top (trans bottom)))) ;;cons the first-row-list with the next-row-list
mat)
(trans thingie)
=> ((1 2 3) (4 5 6) (7 8 9)) ;;wait what?
But, I really want it to be
((1 4 7) (2 5 8) (3 6 9))
What am I doing wrong?
There is a simple way for this:
(defun rotate (list-of-lists)
(apply #'mapcar #'list list-of-lists))
Your attempt is always returning the original mat
. Fix your indentation, and you see that the returned value from the if
form is always thrown away.
Edit: How this works:
List
takes any number of arguments and makes a list of it. Its function definition can be imagined about like this:
(defun list (&rest arguments)
arguments) ; exploit the automatic &rest construction
Mapcar
takes a function and any number of lists, and then makes a
new list of the values created by calling the function always with
one element from those lists. Example: (mapcar #'foo '((A B) (C
D)))
will construct a new list, where the first element is the
result of (foo 'A 'C)
and the second the result of (foo 'B 'D)
.
Apply
takes a spreadable argument list designator as its last
argument. This means that if you give it a list as its last
argument, that list can be "spread" to produce individual arguments
for the function. Example: (apply #'+ '(1 2 3))
has the same
effect as (+ 1 2 3)
.
Now you can expand the line:
(apply #'mapcar #'list '((A B) (C D)))
=>
(mapcar #'list '(A B) '(C D))
=>
(list (list 'A 'C) (list 'B 'D))
=>
'((A C) (B D))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With