Ah, the old ( apply map foo _a_list_ )
trick. Very clever.
In fact (apply map (cons list A))
is the same as (apply map list A)
. That's just how apply
is defined to work.
Trying out some concrete examples usually helps to "get it":
(apply map list '((1 2 3) (10 20 30)) )
=
(apply map (cons list '((1 2 3) (10 20 30))))
=
(apply map (list list '(1 2 3) '(10 20 30) ))
=
( map list '(1 2 3) '(10 20 30) )
=
'((1 10) (2 20) (3 30))
so that the elements of the last argument, '((1 2 3) (10 20 30))
, are spliced in into the wholeapply map ...
form.
Matrix transposition... (list of lists, really).
So you have
(define (mult_mat A B)
(Trans_Mat (map (λ (B_column) (mul_Mat_vec A B_column))
(Trans_Mat B))))
(define (Trans_Mat A)
(apply map list A))
(define (mul_Mat_vec A v)
(map (λ (A_row) (apply + (map * A_row v)))
A))
(define (matrix-multiply A B)
(map
(λ (A_row)
(apply map
(λ B_column
(apply + (map * A_row B_column)))
B))
A))
Notice it's (λ B_column ...
, without parentheses. In ((λ args ...) x y z)
, when the lambda is entered, args
gets all the arguments packaged in a list:
((λ args ...) x y z)
=
(let ([args (list x y z)])
...)
Also notice
(apply map
(λ B_column
(apply + (map * A_row B_column)))
B)
follows the same "tricky" pattern. It's in fact the same as
(apply map (cons
(λ B_column
(apply + (map * A_row B_column)))
B ) )
=
( map
(λ B_column
(apply + (map * A_row B_column)))
B_row1
B_row2
....
B_rowN )
=
(cons (let ([B_column_1 (map car B)])
(apply + (map * A_row B_column_1)))
(map (λ B_column
(apply + (map * A_row B_column)))
(cdr B_row1)
(cdr B_row2)
....
(cdr B_rowN)) )
=
(cons
(apply (λ B_column (apply + (map * A_row B_column)))
(map car B))
(apply map
(λ B_column
(apply + (map * A_row B_column)))
(map cdr B)))
by the definition of map
.
Thus, by applying the map
, the matrix is "opened up" into the list of its elements the rows, and then when the multi-argument map
gets to work on these rows as its arguments, the lambda function gets applied to each row's subsequent numbers, in unison, correspondingly; thus achieving the same effect as the explicit transposition would. But now the added bonus is, we don't need to transpose the result back into the proper form, as we had to with the first version.
This is very clever, and nice.
So, armed with all this understanding, let's try re-reading the original code and see if we can see into it as it is as well.
(define (matrix-multiply matrix1 matrix2)
(map
(λ (row)
(apply map
(λ column ;; <<------ no parens!
(apply + (map * row column)))
matrix2))
matrix1))
This reads: for each row
in matrix1
, multi-arg map
a lambda
over matrix2
. matrix2
is itself also a list of rows; when we multi-arg-map
over the rows, the lambda
gets applied to each column in the rows in turn.
So, for each row in matrix1
, for each column in matrix2
, multiply that row and that column element-wise and sum the results; thus transforming each row into the list of these sums. This obviously works out only if the length of the row and the lengths of each of the columns are the same: if the "width" of the first matrix and the "height" of the second matrix are the same.