你可以通过重新思考你的策略来解决这个问题。
首先,您的基本情况是什么?
在这两种情况下,您都可以将空列表附加到您目前拥有的任何内容中,并称其为好。
接下来,您关心的边缘情况是什么?
在第一种情况下,您只需遍历输入列表的末尾。在第二个中,您从输入列表的开头开始。
好的,让我们尝试实现它,我将使用一个累加器并将调用包装在它周围。
sublist(L1, L2, I, J):-
sublist(L1, Temp, I, J, []),
!,
reverse(Temp, L2).
我们将输入列表L1
,一个要统一的变量作为我们的输出列表L2
,以及索引I
和J
。我正在使用 acut
这样我就不必担心其他解决方案的回溯,并反转它,因为累积的列表是反向构建的。
让我们处理基本情况。
空列表作为输入,只需将累加器与我们的输出列表统一即可。在这一点上,我们不关心指数。事实证明,这也满足了J
超出列表末尾的边缘情况。因为那时我们将把所有的输入列表累加到累加器中,并且还剩下一个 J 值。
sublist([], L2, _I, _J, L2).
I > J 再次将累加器与我们的输出列表统一起来。我们不再关心输入列表。
sublist(_L1, L2, I, J, L2):-
I > J.
现在是边缘情况。
J
超出列表末尾的问题已在上面解决。
I
在列表的开头之前,只需将该索引设置为 0,然后继续。
sublist(L1, L2, I, J, L2):-
I < 0,
sublist(L1, L2, 0, J, L2).
现在我们只需要实现实际的逻辑。我们只想从正确的开始积累I
。因此,让我们递减I
并丢弃输入列表的各个部分,直到我们到达我们想要的位置。为了使索引的末尾匹配,我们也需要递减J
。这样我们在索引之间保持相同的距离。
sublist([_L|Ls], L2, I, J, Acc):-
I > 0,
sublist(Ls, L2, I-1, J-1, Acc).
我们终于到了我们想去的地方。因此,让我们开始使用输入列表中的片段构建列表。这种情况一直持续到我们遇到一个基本案例。之后,累加器返回到原始sublist
子句。
sublist([L|Ls], L2, I, J, Acc):-
sublist(Ls, L2, I, J-1, [L|Acc]).
把它们放在一起,我们最终得到:
sublist(L1, L2, I, J):-
sublist(L1, Temp, I, J, []),
!,
reverse(Temp, L2).
sublist([], L2, _I, _J, L2).
sublist(_L1, L2, I, J, L2):-
I > J.
sublist(L1, L2, I, J, L2):-
I < 0,
sublist(L1, L2, 0, J, L2).
sublist([_L|Ls], L2, I, J, Acc):-
I > 0,
sublist(Ls, L2, I-1, J-1, Acc).
sublist([L|Ls], L2, I, J, Acc):-
sublist(Ls, L2, I, J-1, [L|Acc]).
我们可以像这样测试它:
?- sublist([1,2,3,4,5], S, 0,3).
S = [1, 2, 3, 4].
?- sublist([1,2,3,4,5], S, -1,30).
S = [1, 2, 3, 4, 5].
?- sublist([1,2,3,4,5], S, 3,1).
S = [].
?- sublist([1,2,3,4,5], S, 3,3).
S = [4].
?- sublist([1,2,3,4,5], S, 3,4).
S = [4, 5].