PROLOG-展平段列表

时间:2018-11-08 08:54:08

标签: list prolog

我想整理这样的列表:

[[[a,b],[b,c],[c,d],..]-> [a,b,c ..]

简而言之,我想把每个段[a,b]的标题放在一个扁平化的列表中。

我有当前代码:

%unsplits a list from segments
%e.g [[a,b],[b,c]] becomes [a,b,c].
unSplitList([],_).
unSplitList([[H,_]|T], L) :- append([H], L1, L), unSplitList(T, L1).

但这给了我以下输出:

?- unSplitList([[a,b],[b,c],[c,d]],L).
L = [a, b, c|_1046].

我将如何继续去除那条尾巴(_1046)?在此先感谢:)。

2 个答案:

答案 0 :(得分:3)

这里的问题是下划线:

unSplitList([],_).

这意味着您在“ fly”上构建的列表不以[]结尾,因此保持开放状态。

您可以通过以下方式解决此问题:

unSplitList([], []).

话虽如此,上述内容可以在优雅和效率方面有所改进。在这里,我们可以将append([H], L1, L)替换为L = [H|L1],所以

unSplitList([],[]).
unSplitList([[H,_]|T], [H|L1]) :-
    unSplitList(T, L1).

我们还可以通过用maplist/3 [swi-doc]来写上面的内容来改进上面的内容:

head([H|_], H).

unSplitList(L, Hs) :-
    maplist(head, L, Hs).

请注意,对于包含空子列表(或数字等其他对象)的列表,以上内容无效

答案 1 :(得分:3)

除了@WillemVanOnsem优美的基于maplist / 3的解决方案之外,我还想指出使用DCG描述磁头列表的可能性。

If cell.HasFormula Then
    If cell.HasArray Then
        cell.Offset(0, 1).FormulaArray = "=(" & Right$(cell.FormulaArray, Len(cell.FormulaArray) - 1) & ")/1000"
    Else
        cell.Offset(0, 1).Formula = "=(" & Right$(cell.Formula, Len(cell.Formula) - 1) & ")/1000"
    End If
ElseIf IsNumeric(cell.Value2) Then
    cell.Offset(0, 1).Value2 = cell.Value2 / 1000
End If

此DCG版本与基于Willem的Maplist / 3版本的答案相同:

lists_heads(Ls,Hs) :-      % the list of heads, HS, of the lists in Ls
   phrase(heads(Ls),Hs).   % is described by the DCG heads//1

heads([]) -->              % if there are no lists
   [].                     % there are no heads
heads([L|Ls]) -->          % if L is the first list in the list of lists
   head(L),                % its head is in the list of heads
   heads(Ls).              % followed by the heads of the other lists

head([H|_]) -->            % the head H of the list
   [H].                    % is in the list of heads

正如Willem所指出的,只有当第一个参数不包含空列表时,这些谓词才起作用。您可以轻松地修改DCG head // 1来解决这种情况。出于比较的原因,我将修改后的版本命名为lists_heads2 / 2,仅注释其他DCG规则:

   ?- lists_heads([[a,b],[b,c],[c,d]],L).
L = [a,b,c]
   ?- unSplitList([[a,b],[b,c],[c,d]],L).
L = [a,b,c]

   ?- lists_heads([[a,b,c],[b,c],[c,d]],L).
L = [a,b,c]
   ?- unSplitList([[a,b,c],[b,c],[c,d]],L).
L = [a,b,c]

   ?- lists_heads(Ls,[a,b,c]).
Ls = [[a|_A],[b|_B],[c|_C]] ? ;
no
   ?- unSplitList(Ls,[a,b,c]).
Ls = [[a|_A],[b|_B],[c|_C]]

以下查询说明了两个版本之间的区别:

lists_heads2(Ls,Hs) :-
   phrase(heads2(Ls),Hs).

heads2([]) -->
   [].
heads2([L|Ls]) -->
   head2(L),
   heads2(Ls).

head2([]) -->              % if the list is empty
   [].                     % it contributes no element to the list of heads
head2([H|_]) -->
   [H].

但是,这种额外的灵活性的代价乍一看可能并不明显:由于第一个参数可以包含任意数量的空列表,因此如果在另一个方向上使用,则list_heads2 / 2将循环:

   ?- lists_heads([[],[a,b,c],[],[b,c],[c,d]],L).
no
   ?- lists_heads2([[],[a,b,c],[],[b,c],[c,d]],L).
L = [a,b,c]

为了避免这种情况,如果您在此方向上使用谓词,则必须限制第一个列表的长度:

   ?- lists_heads(Ls,[a,b,c]).
Ls = [[a|_A],[b|_B],[c|_C]] ? ;
no
   ?- lists_heads2(Ls,[a,b,c]).
...                            % <- loops infinitely