矩阵 m 和向量 v 之间的乘法可以这样来完成:取出矩阵 m 中的各列 col ,并计算 col 和向量 v 之间的点积。
取出矩阵各列的操作可以使用 map 函数来完成:
(map (lambda (col)
...)
m)
而矩阵的各个列(它们也是向量)和给定向量 v 之间的点积可以通过 dot-product 进行:
(dot-product col v)
组合起以上两个操作,就得出了 matrix-*-vector 的定义:
;;; 37-matrix-*-vector.scm
(load "37-dot-product.scm")
(define (matrix-*-vector m v)
(map (lambda (col)
(dot-product col v))
m))
测试:
1 ]=> (load "37-matrix-*-vector.scm")
;Loading "37-matrix-*-vector.scm"...
;Warning: Ill-formed file attributes line.
; Loading "37-dot-product.scm"...
; Loading "p78-accumulate.scm"... done
; ... done
;... done
;Value: matrix-*-vector
1 ]=> (define m (list (list 1 2 3 4)
(list 4 5 6 6)
(list 6 7 8 9)))
;Value: m
1 ]=> (define v (list 1 2 3 4))
;Value: v
1 ]=> (matrix-*-vector m v)
;Value 23: (30 56 80)
transpose 函数先取出矩阵内所有列表的 car 部分,将它们组成一个新的列表,作为新矩阵的第一列;接着取出矩阵内所有列表的 cadr 部分,将它们组成一个新的列表,作为新矩阵的第二列,以此类推,一直到整个矩阵处理完为止:
;;; 37-transpose.scm
(load "36-accumulate-n.scm")
(define (transpose m)
(accumulate-n cons '() m))
测试:
1 ]=> (load "37-transpose.scm")
;Loading "37-transpose.scm"...
; Loading "36-accumulate-n.scm"...
; Loading "p78-accumulate.scm"... done
; Loading "36-car-n.scm"... done
; Loading "36-cdr-n.scm"... done
; ... done
;... done
;Value: transpose
1 ]=> (define m (list (list 1 2 3 4)
(list 4 5 6 6)
(list 6 7 8 9)))
;Value: m
1 ]=> (transpose m)
;Value 11: ((1 4 6) (2 5 7) (3 6 8) (4 6 9))
以下是 (transpose m) 执行时产生的调用序列:
(transpose (list (list 1 2 3 4)
(list 4 5 6 6)
(list 6 7 8 9)))
(cons (accumulate cons '() (list 1 4 6))
(accumulate-n cons '() (list (list 2 3 4)
(list 5 6 6)
(list 7 8 9))))
(cons (accumulate cons '() (list 1 4 6))
(cons (accumulate cons '() (list 2 5 7))
(accumulate-n cons '() (list (list 3 4)
(list 6 6)
(list 8 9)))))
(cons (accumulate cons '() (list 1 4 6))
(cons (accumulate cons '() (list 2 5 7))
(cons (accumulate cons '() (list 3 6 8))
(accumulate-n cons '() (list (list 4)
(list 6)
(list 9))))))
(cons (accumulate cons '() (list 1 4 6))
(cons (accumulate cons '() (list 2 5 7))
(cons (accumulate cons '() (list 3 6 8))
(cons (accumulate cons '() (list 4 6 9))
(accumulate-n cons '() (list '() '() '()))))))
(cons (list 1 4 6)
(cons (list 2 5 7)
(cons (list 3 6 8)
(cons (list 4 6 9)
'()))))
(list (list 1 4 6)
(list 2 5 7)
(list 3 6 8)
(list 4 6 9))
矩阵和矩阵之间的乘法规则:对于两个矩阵 m 和 n ,当 m * n 时, mn 的第一列第一行的值为 m 的第一列和 n 的第一行的点积, mn 的第一列第二行的值为 m 的第一列和 n 的第二行的点积,以此类推。
比如说,当 m 为以下矩阵:
(list (list 1 2 3 4)
(list 4 5 6 6)
(list 6 7 8 9))
而 n 为以下矩阵( m 的 transpose ):
(list (list 1 4 6)
(list 2 5 7)
(list 3 6 8)
(list 4 6 9))
那么 mn 的第一列的值可以通过以下方法计算得出:
(let ((col-of-m (car m)))
(list (dot-product col-of-m
(car-n n)))
(dot-product col-of-m
(car-n (cdr-n n)))
(dot-product col-of-m
(car-n (cdr-n (cdr-n n)))))
将以上的方法进行推广,就得出了矩阵乘法函数的定义:
;;; 37-matrix-*-matrix.scm
(load "37-transpose.scm")
(load "37-dot-product.scm")
(define (matrix-*-matrix m n)
(let ((cols (transpose n)))
(map (lambda (col-of-m)
(map (lambda (col-of-cols)
(dot-product col-of-m
col-of-cols))
cols))
m)))
matrix-*-matrix 的定义中没有用到 car-n 和 cdr-n ,取出矩阵 n 各个行的工作由 transpose 和 map 完成:先使用 transpose 将 n 翻转,然后使用 map 对转换后的矩阵的列进行遍历,就可以取出矩阵 n 的各个行了。
测试:
1 ]=> (load "37-matrix-*-matrix.scm")
;Loading "37-matrix-*-matrix.scm"...
;Warning: Ill-formed file attributes line.
; Loading "37-transpose.scm"...
; Loading "36-accumulate-n.scm"...
; Loading "p78-accumulate.scm"... done
; Loading "36-car-n.scm"... done
; Loading "36-cdr-n.scm"... done
; ... done
; ... done
; Loading "37-dot-product.scm"...
; Loading "p78-accumulate.scm"... done
; ... done
;... done
;Value: matrix-*-matrix
1 ]=> (load "37-transpose.scm")
;Loading "37-transpose.scm"...
; Loading "36-accumulate-n.scm"...
; Loading "p78-accumulate.scm"... done
; Loading "36-car-n.scm"... done
; Loading "36-cdr-n.scm"... done
; ... done
;... done
;Value: transpose
1 ]=> (define m (list (list 1 2 3 4)
(list 4 5 6 6)
(list 6 7 8 9)))
;Value: m
1 ]=> (matrix-*-matrix m (transpose m))
;Value 19: ((30 56 80) (56 113 161) (80 161 230))
定义 matrix-*-matrix 的另一种方式是 —— 不直接用 map 处理 m 和 n 的各个向量,而是使用 matrix-*-vector 函数,让矩阵 (transpose n) 去乘 m 的各个 col :
;;; 37-matrix-*-matrix-another.scm
(load "37-transpose.scm")
(load "37-matrix-*-vector.scm")
(define (matrix-*-matrix m n)
(let ((trans-n (transpose n)))
(map (lambda (col-of-m)
(matrix-*-vector trans-n col-of-m))
m)))
这个解法本质上和第一种解法是一样的,只是这个解法相对更高阶一些。