一个适度简短和基本的编写方式,没有findall 和aggregate 等等。
首先,找到两个坐标列表之间的欧几里得距离的谓词:
d([P|Ps], [Q|Qs], D) :-
sum_diff_sq(Ps, Qs, (P-Q)^2, R),
D is sqrt(R).
sum_diff_sq([], [], V, V).
sum_diff_sq([P|Ps], [Q|Qs], V0, V+V0) :-
sum_diff_sq(Ps, Qs, (P-Q)^2, V).
这将计算一对坐标之间的距离,每个坐标都是一个数字列表。
?- d([1], [1], D).
D = 0.0.
?- d([1], [2], D).
D = 1.0.
?- d([1,1], [2,2], D).
D = 1.4142135623730951.
?- d([1,1,1], [2,2,2], D).
D = 1.7320508075688772.
?- d([1,1,1,1], [2,2,2,2], D).
D = 2.0.
然后,计算所有可能的距离:
points_distances([], []).
points_distances([P|Ps], Ds) :-
rest_distances(Ps, P, Ds, Ds0),
points_distances(Ps, Ds0).
points_distances/2 递归地创建列表头和列表尾中每个坐标之间的距离列表(因此每对之间的距离将在结果中)。
rest_distances([], _, Back, Back).
rest_distances([P|Ps], X, [D|Ds], Back) :-
d(P, X, D),
rest_distances(Ps, X, Ds, Back).
这只是计算坐标列表和坐标之间的距离。结果是一个差异列表。
要使用这个:
?- points_distances([[1,1],[1,2],[6,3],[8,2]], D).
D = [1.0, 5.385164807134504, 7.0710678118654755, 5.0990195135927845, 7.0, 2.23606797749979].
?- points_distances([[1,2,3],[2,3,4],[1,7,3],[1,6,3]], D).
D = [1.7320508075688772, 5.0, 4.0, 4.242640687119285, 3.3166247903554, 1.0].
如果您愿意,您可以“保存”哪对坐标彼此相距一定距离。例如,将rest_distances/4的第二个子句的头部从:
rest_distances([P|Ps], X, [D|Ds], Back)
到:
rest_distances([P|Ps], X, [D-pq(X,P)|Ds], Back)
现在,在重新加载程序后,您可以对points_distances/2 的结果进行排序并取其中的第一个元素,就像在另一个答案中一样:
?- points_distances( [[1,2,3],[2,3,4],[1,7,3],[1,6,3]] , D),
keysort(D, [Min_dist-pq(P,Q)|_]).
D = [1.7320508075688772-pq([1, 2, 3], [2, 3, 4]),
5.0-pq([1, 2, 3], [1, 7, 3]),
4.0-pq([1, 2, 3], [1, 6, 3]),
4.242640687119285-pq([2, 3, 4], [1, 7, 3]),
3.3166247903554-pq([2, 3|...], [1, 6|...]),
1.0-pq([1|...], [1|...])],
Min_dist = 1.0,
P = [1, 7, 3],
Q = [1, 6, 3].