啊,老 ( apply map foo _a_list_ ) 把戏。非常聪明。
实际上(apply map (cons list A)) 和(apply map list A) 是一样的。这就是 apply 的定义方式。
尝试一些具体的例子通常有助于“理解”:
(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))
这样最后一个参数'((1 2 3) (10 20 30)) 的元素被拼接到整个apply map ... 形式中。
矩阵转置(确实是列表的列表)。
所以你有
(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))
注意它是(λ B_column ...,没有括号。在((λ args ...) x y z) 中,当输入 lambda 时,args 将所有参数打包到一个列表中:
((λ args ...) x y z)
=
(let ([args (list x y z)])
...)
另请注意
(apply map
(λ B_column
(apply + (map * A_row B_column)))
B)
遵循相同的“棘手”模式。其实是一样的
(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)))
根据map的定义。
因此,通过应用map,矩阵被“打开”到其元素列表中的行,然后当多参数map开始处理这些rows 作为其参数,lambda 函数相应地一致地应用于每一行的后续数字;从而达到与 显式转置 相同的效果。但现在额外的好处是,我们不需要像第一个版本那样将结果转回正确的形式。
这是非常聪明,很好。
因此,有了所有这些理解,让我们尝试重新阅读原始代码,看看我们是否也能看到它。
(define (matrix-multiply matrix1 matrix2)
(map
(λ (row)
(apply map
(λ column ;; <<------ no parens!
(apply + (map * row column)))
matrix2))
matrix1))
这读作:对于matrix1 中的每个row,多参数map 一个lambda 超过matrix2。 matrix2 本身也是一个行列表;当我们在行上使用 multi-arg-map 时,lambda 会依次应用于行中的每一列。
因此,对于matrix1 中的每个行,对于matrix2 中的每个列,将该行和该列逐元素相乘,然后将结果相加;从而将每一行转换为这些总和的列表。这显然只有在行的长度和每列的长度相同时才有效:如果第一个矩阵的“宽度”和第二个矩阵的“高度”相同。