如何在Prolog中转置一个矩阵

9

我该如何将列表 [[1,2,3][4,5,6][6,7,8]] 转置为 [[1,4,6],[2,7,8],[3,6,9]]

简单描述一下:我想将矩阵向左旋转90度。我该怎么做?

8个回答

10

不确定你的例子是否正确,但我明白你的意思。

如果使用SWI-PROLOG,可以使用CLPFD模块,像这样:

:- use_module(library(clpfd)).

这将允许您使用transpose/2谓词,像这样:

1 ?- transpose([[1,2,3],[4,5,6],[6,7,8]], X).
X = [[1, 4, 6], [2, 5, 7], [3, 6, 8]].

否则(如果没有SWI-PROLOG),您可以使用这个实现(它恰好是SWI的clpfd中的旧实现):
transpose([], []).
transpose([F|Fs], Ts) :-
    transpose(F, [F|Fs], Ts).

transpose([], _, []).
transpose([_|Rs], Ms, [Ts|Tss]) :-
        lists_firsts_rests(Ms, Ts, Ms1),
        transpose(Rs, Ms1, Tss).

lists_firsts_rests([], [], []).
lists_firsts_rests([[F|Os]|Rest], [F|Fs], [Os|Oss]) :-
        lists_firsts_rests(Rest, Fs, Oss).

如果您想使用 foldl 和 maplist 内置函数的更新版本,请参见 clpfd.pl


谢谢!我正在使用SWI Prolog并尝试了您的第一个解决方案。但是当我输入"use_module(library(clpfd))."时,我收到以下错误消息: "ERROR: source_sink `library(clpfd)' does not exist" 您知道如何解决这个问题吗? - cody
我猜你可能没有安装 clpfd 库,或者你的 SWI-PL 安装有问题。clpfd.pl 应该位于 SWI-PL 主目录下的 library/clp 目录中。如果它在那里,那么也许 SWI-PL 找不到它;在这种情况下,也许你的 pl 可执行文件与 SWI-PL 根目录中的 ___.rc 配置文件名称不同(它们必须相同)。否则,只需使用我发布的定义,这是 clpfdtranspose/2 实现。 - user206428
好的。库文件丢失了,我把它复制到了library/clp目录下。但是它还是无法工作。在pl-root目录下有一个名为"plwin.rc"的文件,以及一个包含可执行文件的bin文件夹。将.rc文件移动到bin文件夹中并不能解决问题...所以我需要修改它吗? - cody
如果最初缺少库,则您的SWI-PL副本可能未配置为在该位置检查库,因此仅仅复制文件可能不会有太大作用(您可能存在配置问题,我不确定如何在没有看到您的系统的情况下解决)。尝试升级到最新版本:http://www.swi-prolog.org/download/stable - user206428
@sharky:好答案,+1!现在 library(clpfd) 中有新代码可以更优雅地完成此操作。源代码的链接也已更改。希望您能相应更新答案。 - mat
@mat 谢谢。我已经提供了 clpfd 中更新的实现链接,但我在我的答案中保留了旧的实现,因为我更喜欢它(它不使用其他 Prolog 实现可能没有的内置函数)。 - user206428

7

这是我能想到的最小解决方案。

代码

transpose([[]|_], []).
transpose(Matrix, [Row|Rows]) :- transpose_1st_col(Matrix, Row, RestMatrix),
                                 transpose(RestMatrix, Rows).
transpose_1st_col([], [], []).
transpose_1st_col([[H|T]|Rows], [H|Hs], [T|Ts]) :- transpose_1st_col(Rows, Hs, Ts).

测试

:- transpose([[1,2,3],
              [4,5,6],
              [7,8,9]], R),
   print(R).

输出:

[[1,4,7],
 [2,5,8],
 [3,6,9]]

说明

它的工作原理是,transpose会递归调用transpose_1st_col,该函数会提取和转置矩阵的第一列。例如:

:- transpose_1st_col([[1,2,3],
                      [4,5,6],
                      [7,8,9]], Row, RestMatrix),
   print(Row),
   print(RestMatrix).

将被打印

[1,4,7]

并且

[[2,3],
 [5,6],
 [8,9]]

重复这个步骤,直到输入矩阵为空,在这个时候所有列都已经被转置。然后将转置的列连接起来形成转置矩阵。


2

这里是一个较大回答的片段:

% transposed(+A, ?B) iff matrix B is transposed matrix A
transposed(A, B) :- transposed(A, [], B).
transposed(M, X, X) :- empty(M), !.
transposed(M, A, X) :- columns(M, Hs, Ts), transposed(Ts, [Hs|A], X).

% empty(+A) iff A is empty list or a list of empty lists
empty([[]|A]) :- empty(A).
empty([]).

% columns(+M, ?Hs, ?Ts) iff Hs is the first column
%   of matrix M and Ts is the rest of matrix M
columns([[Rh|Rt]|Rs], [Rh|Hs], [Rt|Ts]) :- columns(Rs, Hs, Ts).
columns([[]], [], []).
columns([], [], []).

谢谢 :) 或许这可能会有用。首先,如果我能在某种程度上使其工作,我更喜欢使用transpose/2函数... - cody

1
更简单的方法:
trans(M, [P|T]):- first(M, P, A), trans(A, T).
trans(Empty, []):- empty(Empty).

empty([[]|T]):- empty(T).
empty([[]]).

first([[P|A]|R], [P|Ps], [A|As]):- first(R, Ps, As).
first([], [], []).

高效也。
[debug] 36 ?- time(trans([[1,2,3],[4,5,6],[7,8,9]],A)).
% 21 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
A = [[1,4,7],[2,5,8],[3,6,9]] ;
% 12 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
false.

1
把简单的情况放在最后有什么原因吗? - false
我无法对结果进行排序。 - Ricardo Zorio

1
另一种简单的方法:

transpose(M0, M) :-
    nonvar(M0),
    findall(L, maplist(nth1(_), M0, L), M).

?- transpose([[1,2,3],[4,5,6],[7,8,9]], M). 
M = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]. `

0

一种迭代的方法:

trans([H|R],[H1|R1]):-trans2([H|R],[H|R],[],[H1|R1],0),!.
trans2([A|_],_,_,[],N):-length(A,N).
trans2(M,[],H1,[H1|R1],N):-N1 is N+1, trans2(M,M,[],R1,N1).
trans2(M,[H|R],L,[H1|R1],N):-nth0(N,H,X),
   append(L,[X],L1),trans2(M,R,L1,[H1|R1],N).

0

为了更好的理解,这是我的解决方案,包含完整的名称:

% emptyMatrix(Line, EmptyMatrix)
emptyMatrix([],[]).
emptyMatrix([_|T1],[[]|T2]):-emptyMatrix(T1,T2).
% only length of parameter 'Line' is interesting. It ignores its content.    

% appendElement(Element, InputList, OutputList)
appendElement(E,[],[E]).
appendElement(E,[H|T],[H|L]):-appendElement(E,T,L).

% appendTransposed(NestedList, InputMatrix, OutputMatrix)
appendTransposed([],[],[]).
appendTransposed([X|T1],[],[[X]|T3]):-appendTransposed(T1,[],T3).
appendTransposed([X|T1],[R|T2],[C|T3]):-appendElement(X,R,C),appendTransposed(T1,T2,T3).

% transposeMatrix(InputMatrix, TransposedMatrix)
transposeMatrix([L|M],T):-emptyMatrix(L,A),transpose([L|M],T,A).
transpose([],T,T).
transpose([L|M],T,A):-appendTransposed(L,A,B),transpose(M,T,B).

“行”可以是列或行。

这个想法在于将元素附加到空矩阵的列表中。 (例如,第一行的所有元素=所有列的第一个元素 => 第i行的所有元素=所有列的第i个元素)

在我的机器上运行良好,如此会话协议向我展示:

5 ?- transposeMatrix([[1,2],[3,4]],T).
T = [[1, 3], [2, 4]] ;
false.

6 ?- transposeMatrix([[1],[2]],T).
T = [[1, 2]] ;
false.

7 ?- transposeMatrix([[1,2,3],[4,5,6]],T).
T = [[1, 4], [2, 5], [3, 6]] ;
false.

8 ?- transposeMatrix([[1]],T).
T = [[1]] ;
false.

0

另一种方法:

delete_one_list([], []).
delete_one_list([[_|L]|LLs], [L|Ls]) :-
  delete_one_list(LLs, Ls).

transpose_helper([], []).
transpose_helper([[X|_]|Xs], [X|Ys]) :-
  transpose_helper(Xs, Ys).

transpose([[]|_], []).
transpose(List, [L|Ls]) :-
  transpose_helper(List, L),
  delete_one_list(List, NewList),
  transpose(NewList, Ls).

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接