转载自:https://mp.weixin.qq.com/s/G1rBTwds5bkObtQfHq5YDw
(注意:程序段行首的数字是为了注释单独编制的行号,后面的注释是对应行号写的)
10、列表的游程编码
encode(L1,L2):-通过游程长度编码从列表L1获得列表L2。
元素的连续重复项被编码为项[N,E],其中N是元素E的重复项数。
1 encode(L1,L2) :-
2 pack(L1,L),
3 transform(L,L2).
4
5 transform([],[]).
6 transform([[X|Xs]|Ys],[[N,X]|Zs]) :-
7 length([X|Xs],N),
8 transform(Ys,Zs).
注释行:
1.L1编码后的列表为L2
2. 如果列表L1 是各自打好包的列表的列表L
3. 并且L2是列表L编码后的列表
4.
5. 空列表的编码列表是空列表
6.
7. 算好包中(列表)元素的长度N,和[N,X]合取
8. 继续编码后续包(递归)
?- consult('p1_09.pl').
true.
?- consult('p1_10.pl').
true.
?- encode([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).
X = [[4, a], [1, b], [2, c], [2, a], [1, d], [4, e]] .
11、列表的游程编码衍生版本
通过游程(run-length)编码从列表L1获得列表L2。元素的连续重复项被编码为
项[N,E],其中N是元素E的重复项数。
但是,如果N等于1,则将元素简单地复制到输出列表中。
1 encode_modified(L1,L2) :-
2 encode(L1,L),
3 strip(L,L2).
4
5 strip([],[]).
6 strip([[1,X]|Ys],[X|Zs]) :-
7 strip(Ys,Zs).
8 strip([[N,X]|Ys],[[N,X]|Zs]) :-
9 N > 1,
10 strip(Ys,Zs).
注释行:
1. L1编码后的列表为L2
2. 如果列表L1 是各编码好包的列表的列表L
3. 并且L2是列表L编码后的列表
4.
5. 空列表的编码列表是空列表
6. 当N等于1([1,X]|Ys])时直接将元素放入主表内不另列
7. 继续编码后续包(递归)
8. 取[[X|Xs]|Ys]列表中的打好的包
9. 算好包中(列表)元素的长度N,和[N,X]合取
10.继续编码后续包(递归)
?- consult('p1_09.pl').
true.
?- consult('p1_10.pl').
true.
?- consult('p1_11.pl').
true.
?- encode_modified([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).
X = [[4, a], b, [2, c], [2, a], d, [4, e]] .
12、解码游程压缩列表
decode(L1,L2) 将列表L1解码到列表L2
1 decode([],[]).
2 decode([X|Ys],[X|Zs]) :-
3 \+ is_list(X),
4 decode(Ys,Zs).
5 decode([[1,X]|Ys],[X|Zs]) :-
6 decode(Ys,Zs).
7 decode([[N,X]|Ys],[X|Zs]) :-
8 N > 1,
9 N1 is N - 1,
10 decode([[N1,X]|Ys],Zs).
注释行:
1. 空列表解码后的列表仍为空列表
2. 列表[X|Ys]解码后的列表为[X|Zs]
3. 如果元素X不是列表,将元素X直接放入[X|Zs]中
4. 继续解码后续包
5. 如果要解码包列表的首元素是1则将元素X直接放入[X|Zs]中
6. 继续解码后续包
7. 列表[[N,X]|Ys]解码后的列表为[X|Zs]
8. 如果N大于1
9. 并且 N减以后赋值给N1,
10.继续递归剩余部分
?- consult(p1_12).
true.
?- decode([[4, a], [1, b], [2, c], [2, a], [1, d], [4, e]],X),write('X = '),write(X),nl.
X = [a,a,a,a,b,c,c,a,a,d,e,e,e,e]
X = [a, a, a, a, b, c, c, a, a|...] .
?- decode([[4, a], b, [2, c], [2, a], d, [4, e]],X),write('X = '),write(X),nl.
X = [a,a,a,a,b,c,c,a,a,d,e,e,e,e]
X = [a, a, a, a, b, c, c, a, a|...] .
13、列表的游程长度编码(直接解决方案)
encode_direct(L1,L2):- 通过游程长度编码从列表L1获得列表L2。元素的连续重复项被编码为项[N,E],其中N是元素E的重复项数。但是,如果N等于1,则该元素将被简单地复制到输出列表中。
1 encode_direct([],[]).
2 encode_direct([X|Xs],[Z|Zs]) :-
3 count(X,Xs,Ys,1,Z),
4 encode_direct(Ys,Zs).
注释行:
1.
2. X是指一组连续相同元素的那个元素
3. 处理X那组元素,Ys是剩余元素组的列表
4. 递归后续元素组
count(X,Xs,Ys,K,T)Ys是在删除X的所有前导副本时仍保留在列表Xs中的列表。T是项[N,X],其中N是K加上可以从Xs中删除的X的数目。在N=1的情况下,T是X,而不是项[1,X]。
1 count(X,[],[],1,X).
2 count(X,[],[],N,[N,X]) :-
3 N > 1.
4 count(X,[Y|Ys],[Y|Ys],1,X) :-
5 X \= Y.
6 count(X,[Y|Ys],[Y|Ys],N,[N,X]) :-
7 N > 1,
8 X \= Y.
9 count(X,[X|Xs],Ys,K,T) :-
10 K1 is K + 1,
11 count(X,Xs,Ys,K1,T).
注释行:
1. X是最后一组元素,当X元素个数为1时
2. X是最后一组元素,当X元素个数为1时
3. X也是整个表的最后一个元素
4. 当只有一个元素时,设置值为1
5. 如果当前元素与后一个元素不等则终止条件成立
6.
7. 如果X这组元数的个数不止一个元素时,
8. 并且此时是最后一个元素时终止条件成立
9. 元素相同时
10. 如果统计数增1
11.然后继续递归遍历下一个相同元素
?- consult('p1_13.pl').
true.
?- encode_direct([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).
X = [[4, a], b, [2, c], [2, a], d, [4, e]] .
14、重复列表中的元素
dupli(L1,L2) :- L1为原列表,L2为是将L1中每个元素重复一次后的表
1 dupli([],[]).
2 dupli([X|Xs],[X,X|Ys]) :-
3 dupli(Xs,Ys).
注释行:
1. 空列表重复后的列表任然为空列表
2. 将列表[X|Xs]中的元素X放两个到后面的列表中如[X,X|Ys]
3. 递归后续列表中的元素
?- consult('p1_14.pl').
true.
?- dupli([a,b,c,c,d],X).
X = [a, a, b, b, c, c, c, c, d|…].
?- dupli(X,[a,a,b,b,c,c,c,c,d,d]).
X = [a, b, c, c, d].
15、重复给定列表元素的次数
dupli(L1,N,L2) :-L2是通过将所有元素复制N次而从L1获得的。
1 dupli(L1,N,L2) :-
2 dupli(L1,N,L2,N).
dupli(L1,N,L2,K) :- L2是通过将L1的每个元素复制N倍,K作为递归时的计数。
3 dupli([],_,[],_).
4 dupli([_|Xs],N,Ys,0) :-
5 dupli(Xs,N,Ys,N).
6 dupli([X|Xs],N,[X|Ys],K) :-
7 K > 0,
8 K1 is K - 1,
9 dupli([X|Xs],N,Ys,K1).
?- consult('p1_15.pl').
true.
注释行:
1. 列表L2是列表L1中相应元素的N倍个
2. 如果列表L2是列表L1中相应元素的N倍个,第四个参数N作为过度数
3. 空列表的N倍元素依然是空列表
4. 当某元素增倍计数为0
5. 递归开启下一个元素
6. 倍增首元素
7. 如果K大于0
8. 并且K减小1
9. 接着递归增加元素
?- dupli([a,b,c,c,d],4,X),write('X = '),write(X),nl.
X = [a,a,a,a,b,b,b,b,c,c,c,c,c,c,c,c,d,d,d,d]
X = [a, a, a, a, b, b, b, b, c|...] .
16、从列表中删除第N个元素
drop(L1,N,L2):-删除列表L1中的第N个元素得到的新表是L2。
1 drop(L1,N,L2) :-
2 drop(L1,N,L2,N).
drop(L1,N,L2,K):-通过首先复制K-1元素从L1获得L2,然后删除一个元素,此后,删除第N个元素。
3 drop([],_,[],_).
4 drop([_|Xs],N,Ys,1) :-
5 length(Xs, R),
6 drop(Xs,N,Ys,R).
7 drop([X|Xs],N,[X|Ys],K) :-
8 K > 1,
9 K1 is K - 1,
10 drop(Xs,N,Ys,K1).
注释行:
1. 词谓词成功
2. 如果此谓词成功
3. 空列表不管删除什么目标列表也都是空列表
4. 到达指定位置成立
5. 获取剩余元素个数
6. 忽略指定的元素继续递归调用成功
7. 没到达指定位置将当前元素加入到新表
8. 确定如果没到达指定位置
9. 指定位置标识K减去1后赋值给K1
10.继续递归处理后续的元素
?- consult('p1_16.pl').
true.
?- drop([1,2,3,4,5,6,7,8,9],3,X).
X = [1, 2, 4, 5, 7, 8] .
17、将列表分为两部分
split(L,N,L1,L2) :- 列表L1包含列表L的前N个元素,列表L2包含其余元素。
1 split(L,0,[],L).
2 split([X|Xs],N,[X|Ys],Zs) :-
3 N > 0,
4 N1 is N - 1,
5 split(Xs,N1,Ys,Zs).
注释行:
1. 列表L从位置0开始分则前一列表为空,后一列表也就是它本身
2. 取列表[X|Xs]中的元素X,将X放到列表
3. 确定N大于0
4. 然后 N减掉1 赋值给N1
5. 继续递归处理列表中下一个元素
?- consult('p1_17.pl').
true.
?- split([a,b,c,d,e,f,g,h,i,j,k],3,L1,L2).
L1 = [a, b, c],
L2 = [d, e, f, g, h, i, j, k] .
18、从列表中提取一个切片。
slice(L1,I,K,L2) :- 给定两个索引I和K,切片是包含原始列表的第I和第K个元素之间的元素的列表(包括两个限制)。从1开始计算元素。
1 slice([X|_],1,1,[X]).
2 slice([X|Xs],1,K,[X|Ys]) :-
3 K > 1,
4 K1 is K - 1,
5 slice(Xs,1,K1,Ys).
6 slice([_|Xs],I,K,Ys) :-
7 I > 1,
8 I1 is I - 1,
9 K1 is K - 1,
10 slice(Xs,I1,K1,Ys).
注释行:
1. 将列表[X|_]从1到1切片得到的子列表为[X]
2. 将列表[X|Xs]从1到K切片得到的子列表为[X|Ys](取元素X到新列表)
3. 确保K大于1,
4. K减1 赋值给K1
5. 递归处理余下的元素
6. 将列表[_|Xs]从I到K切片得到的子列表为Ys
7. 确保I大于0
8. 然后I减1赋值给I1
9. 同时将K减1的值赋值给K1
10.继续递归处理后续元素(这里调用第二个子句)
?- consult('p1_18.pl').
true.
?- slice([a,b,c,d,e,f,g,h,i,j,k],4,8,L).
L = [d, e, f, g, h] .
19、将列表循环移N个位置当N值为整循环左移,负值时为循环右移
:- ensure_loaded(p1_17).
1 rotate([],_,[]) :-
2 !.
3 rotate(L1,N,L2) :-
4 length(L1,NL1),
5 N1 is N mod NL1,
6 split(L1,N1,S1,S2),
7 append(S2,S1,L2).
注释行:
1.
2.
3. 列表L1循环移位N后的列表为L2
4. 确定列表L1的长度为NL1
5. 求N与NL1的余数赋值给N1 注意 (-3 mod 5 = 2)
6. 按求得的N1值将列表L1分成两个列表S1和S2
7. 将列表S2后接S1生成表L2
?- consult('p1_19.pl').
true.
?- rotate([a,b,c,d,e,f,g,h],3,X).
X = [d, e, f, g, h, a, b, c] .
?- rotate([a,b,c,d,e,f,g,h],-2,X).
X = [g, h, a, b, c, d, e, f] .
20、删除列表中指定元素。
当列表[X|Xs]的首元素是指定元素时成功,第4个参数将忽略指定元素X
1 remove_at(X,[X|Xs],1,Xs).
2 remove_at(X,[Y|Xs],K,[Y|Ys]) :-
3 K > 1,
4 K1 is K - 1,
5 remove_at(X,Xs,K1,Ys).
注释行:
1.
2. 当列表[Y|Xs]的首元素不是指定元素(K不等于1)
3. 确定K是大于1的
4. 将K减1的值赋给K1
5. 继续递归后续元素
?- consult('p1_20.pl').
true.
?- remove_at(X,[a,b,c,d,e,f,g,h],4,R).
X = d,
R = [a, b, c, e, f, g, h] .
21、将元素插入到列表的指定位置
:
-
ensure_loaded(p1_20).
%将元素X插入到列表L中的第K位,结果列表为R
insert_at(X,L,K,R) :-
remove_at(X,R,K,L).
%列表L是删除列表R指定第K位的元素X后的表
?- consult('p1_21.pl').
true.
?- insert_at(1234,[a,b,c,d],2,L).
L = [a, 1234, b, c, d] .
22、创建一个给定整数范围的自然数列表
1 range(I,I,[I]).
2 range(I,K,[I|L]) :-
3 I < K,
4 I1 is I + 1,
5 range(I1,K,L).
注释行:
1. 从I开始到I结束的列表为[I]
2. 从I开始到K结束的列表为[I|L]
3. 确保 I 小于 K
4. 将I增1后的值赋给I1
5. 递归继续处理后续自然数
?- consult('p1_22.pl').
true.
?- range(4,9,L).
L = [4, 5, 6, 7, 8, 9] .
23、取出列表中指定数量的元素,并且这些元素是随机取的
:- ensure_loaded(p1_20).
1 rnd_select(_,0,[]).
2 rnd_select(Xs,N,[X|Zs]) :-
3 N > 0,
4 length(Xs,L),
5 I is random(L) + 1,
6 remove_at(X,Xs,I,Ys),
7 N1 is N - 1,
8 rnd_select(Ys,N1,Zs).
注释行:
1. 取0个元素的列表是空列表
2. 将从列表Xs取出的元素X添加到列表[X|Zs]中
3. 确保N大于0
4. 计算列表Xs的长度为L(元素的个数为0)
5. 插身随机数(+1是避免随机数是0)
6. 移除列表Xs中指定位置为I,的元素X
7. 将N减1的值赋给N1
8. 继续递归取后续元素谓词
?- consult('p1_23.pl').
true.
?- rnd_select([a,b,c,d,e,f,g,h,1,2,3,4,5],4,L).
L = [h, 4, d, b] .
24、lotto 从1..M中抽取N个不同的随机数。
1 :- ensure_loaded(p1_22).
2 :- ensure_loaded(p1_23).
3
4 lotto(N,M,L) :-
5 range(1,M,R),
6 rnd_select(R,N,L).
注释行:
1. 引入谓词range/3
2. 引入谓词rnd_select/3
3.
4. 从范围1到M范围取出N个随机元素放入列表L中
5. 产生1到M范围的自然数放在列表R中
6. 从列表R中取N个随机位置的元素
?- consult('p1_24.pl').
true.
?- lotto(6,49,L).
L = [34, 46, 44, 18, 40, 3] .
25、将指定列表中的元素打乱后随机排放
1 ensure_loaded(p1_23).
2
3 rnd_permu(L1,L2) :-
4 length(L1,N),
5 rnd_select(L1,N,L2).
注释行:
1. 引入谓词rnd_select/3
2.
3. 将列表L1的元素重新随机排放到列表L2
4. 获取整个表的长度
5. 指定列表L1中的所有元素N重新随机排列到列表L2
?- consult('p1_25.pl').
true.
?- rnd_permu([a,b,c,d,e,f,1,2,3,4,5,6],L),write('L = '),write(L),nl.
L = [3,4,a,c,e,2,6,b,1,f,d,5]
L = [3, 4, a, c, e, 2, 6, b, 1|...] .
26、生成从列表的N个元素中选择的K个不同对象的组合
combination(K,L,C) :- C是从列表L中选择的K个不同元素的列表
1 combination(0,_,[]).
2 combination(K,L,[X|Xs]) :-
3 K > 0,
4 el(X,L,R),
5 K1 is K-1,
6 combination(K1,R,Xs).
%分析下面谓词el/3的作用。
7 el(X,[X|L],L).
8 el(X,[_|L],R) :-
9 el(X,L,R).
注释行:
1. 0个组合的任何列表产生的列表都是空列表
2. 每个组合的个数为K,从列表L中取,组合好的表放到[X|Xs]
3. 确保K大于0
4. 获取元素X,获取剩余元素的列表R
5. 将 K 减 1的值赋给K1
6. 递归处理后续元素
7. 将列表[X|L]解析为元素X,和列表L
8. 忽略列表中[_|L]的’_’匿名元素
9. 继续递归处理后续元素
?- consult('p1_26.pl').
true.
?- combination(3,[a,b,c,d,e,f],L).
L = [a, b, c] ;
L = [a, b, d] ;
L = [a, b, e] ;
L = [a, b, f] .
27、将集合的元素分组为不相交的子集
将列表G的9个元素分配到列表G1,G2和G3中,这样列表G1,G2和G3分别包含2,3和4个元素
1 group3(G,G1,G2,G3) :-
2 selectN(2,G,G1),
3 subtract(G,G1,R1),
4 selectN(3,R1,G2),
5 subtract(R1,G2,R2),
6 selectN(4,R2,G3),
7 subtract(R2,G3,[]).
注释行:
1.
2. 从列表G中获取有2个元素的列表G1
3. 从列表G获取除列表G1中的元素的剩余元素的列表R1
4. 从列表R1中获取有3个元素的列表G2
5. 从列表R1获取除列表G2中元素的剩余元素的列表R1
6. 从列表R2中获取有4个元素的列表G3
7. 从列表R2中获取除列表G3中元素的剩余元素的列表是空列表
返回所有可能的选择,但要避免排列;也就是说,在生成S = [a,b,c]之后,不要返回S = [b,a,c],依此类推。
9 selectN(0,_,[]) :- !.
10 selectN(N,L,[X|S]) :-
11 N > 0,
12 el(X,L,R),
13 N1 is N-1,
14 selectN(N1,R,S).
注释行:
8.
9. 从任意列表中取0个元素,得到的列表一定是空列表
10.从列表L中取N个元素,将得到的元素放到列表[X|S]中(调用一次取一个元素)
11.确保N大于0
12. 将元素X和表R从L中解析出来
13.将N减1后把值赋给N1
14.递归继续处理后面的元素
% Use el/3 from p1_26:
:- ensure_loaded(p1_26).
% el(X,[X|L],L).
% el(X,[_|L],R) :- el(X,L,R).
% subtract/3 是预定义谓词
将G的元素分布到组Gs中。组的大小在列表Ns中给出。
15 group([],[],[]).
16 group(G,[N1|Ns],[G1|Gs]) :-
17 selectN(N1,G,G1),
18 subtract(G,G1,R),
19 group(R,Ns,Gs).
注释行:
15.空列表,按第二参数空列表分组后的列表任然是空列表
16.将列表G,按第2参数标识的数字分配成参数3列表的列表
17.从列表G中获取有N1个元素的列表G1
18.从列表G获取除列表G1中的元素的剩余元素的列表R
19. 继续递归处理剩余元素列表R
?- consult('p1_27.pl').
true.
?- group3([ 赵,钱,孙,李,周,吴,郑,王,张],G1,G2,G3).
G1 = [赵, 钱],
G2 = [孙, 李, 周],
G3 = [吴, 郑, 王, 张] ;
G1 = [赵, 钱],
G2 = [孙, 李, 吴],
G3 = [周, 郑, 王, 张] .
?- group([赵,钱,孙,李,周,吴,郑,王,张],[2,2,5],Gs).
Gs = [[赵, 钱], [孙, 李], [周, 吴, 郑, 王, 张]] ;
Gs = [[赵, 钱], [孙, 周], [李, 吴, 郑, 王, 张]] ;
Gs = [[赵, 钱], [孙, 吴], [李, 周, 郑, 王, 张]] .