我必须编写一个谓词,该谓词product/3接收两个矩阵并在可能的情况下返回它们的矩阵乘法,否则会失败。(这意味着如果矩阵满足要求[n x p] [p x y],则返回与维度的乘法[n x y])
例子:
product(M1, M2, R)
?- product([[1,2],[3,4],[5,6]], [[1,1,1],[1,1,1]], M).
M = [[3, 3, 3], [7, 7, 7], [11, 11, 11]];
No
Run Code Online (Sandbox Code Playgroud)
为此,我有两个代码,它们索引矩阵上的第 n 行rowI,索引第 n 列columnI(我在下面的代码中解释了它们的工作原理)。
%Predicate: rowI(M, I, RI)
%Input rowI([[1,2],[3,4],[5,6]], 2, RI).
% RI = [3,4];
rowI([H|_],1,H):-!.
rowI([_|T],I,X) :-
I1 is I-1,
rowI(T,I1,X).
% columnJ(M, J, CJ)
%Input columnJ([[1,2],[3,4],[5,6]], 1, CJ).
% CJ = [1,3,5];
columnJ([],_,[]).
columnJ([H|T], I, [R|X]):-
rowI(H, I, R),
columnJ(T,I,X).
product([H|T], M2, [R|X]):-
columnJ(M2, C, Z),
mult(H, Z , X),
product(T, M2 , X).
Run Code Online (Sandbox Code Playgroud)
我想以某种方式抓住M1(这将是每一行)的头部,然后乘以每一列M2,在添加乘法之后,这个列表将是新行。所以(C 必须是一个从 1 到长度的计数器M2,然后mult我只是想让它乘以列表。(此时没有定义 mult,只是一个猜测)。
在这里,我试图解释我的想法......但可能有一种更简单的方法。你怎么认为?
紧凑的代码(借助高阶构造 maplist 和 foldl)。我故意留下未评估的表达式,因此结果可以在更一般的上下文中重用:
:- module(matrix_multiply,
[matrix_multiply/3
,dot_product/3
]).
:- use_module(library(clpfd), [transpose/2]).
%% matrix_multiply(+X,+Y,-M) is det.
%
% X(N*P),Y(P*M),M(N*M)
%
matrix_multiply(X,Y,M) :-
transpose(Y,T),
maplist(row_multiply(T),X,M).
row_multiply(T,X,M) :-
maplist(dot_product(X),T,M).
dot_product([X|Xs],[T|Ts],M) :-
foldl(mul,Xs,Ts,X*T,M).
mul(X,T,M,M+X*T).
Run Code Online (Sandbox Code Playgroud)
编辑
用法(保存在名为 matrix_multiply.pl 的文件中):
?- [matrix_multiply].
?- matrix_multiply([[1,2],[3,4],[5,6]], [[1,1,1],[1,1,1]],R),maplist(maplist(is),C,R).
R = [[1*1+2*1, 1*1+2*1, 1*1+2*1], [3*1+4*1, 3*1+4*1, 3*1+4*1], [5*1+6*1, 5*1+6*1, 5*1+6*1]],
C = [[3, 3, 3], [7, 7, 7], [11, 11, 11]].
Run Code Online (Sandbox Code Playgroud)
数值计算由 明确要求,maplist(maplist(is),C,R)。R 保存符号表达式,C 保存值。
编辑
只是要注意 clpfd:transpose 的依赖很容易删除:这是一个基于 nth/3 和 library(yall) 的替代“单行”定义
mat_transpose([R1|Rs],T) :- findall(V,(
nth1(Col,R1,_),
maplist({Col}/[R,C]>>nth1(Col,R,C),[R1|Rs],V)),T).
Run Code Online (Sandbox Code Playgroud)