【问题标题】:Prolog Recursion removing elements at index that is multiples of n from a list where n is any numberProlog递归从列表中删除索引处的元素是n的倍数,其中n是任意数字
【发布时间】:2015-03-05 23:20:30
【问题描述】:

这是我第一次在这里提出问题,但是我有一个问题,我真的无法解决这个问题,即 Prolog 递归,尤其是在处理列表时。所以我应该解决的任务是编写一个像这样工作的 drop 谓词。例如,drop([1,2,3,4,5,6,7,8,9], 2, L) 其中L = [1,3,5,7,9]N=n 其中位置n、2n、3n....的元素将被删除。列表从 1 开始是另一件需要注意的事情。

这是我目前的尝试和思考过程:

drop([], _, []).

indexOf([X|_], X, 1). %Using 1 because the question says the first element starts from 1.

indexOf([_|Ys], Y , I):-
    indexOf(Ys, Y, N),
    I is N + 1.

drop([X|Xs], Y, [X|_]) :-
    indexOf([X|Xs] , X , A),
    Z is A mod Y,
    Z \== 0.

drop([X|Xs], Y, Zs) :-
    %indexOf([X|Xs], X, A),
    drop(Xs, Y, Zs).

我创建了一个 indexOf 谓词来查找从 1 开始的元素的索引。接下来,我的想法是使用我的第一个 drop 递归案例(在上面的代码中是第 5 个案例)来检查元素的位置在除以 Y(第二个输入)时是否返回零余数.如果它不返回零的余数,则 X 将保留在列表中并且不会被删除。然后,prolog 移动到第二个 drop 递归案例,该案例只能在 Z=0 时到达,它将从列表中删除 X 以返回 Zs。本质上,如果由indexOf 返回的索引为 n、2n、3n... 的元素在除以 Y(第二个输入)时未返回零余数,则将被删除。

目前我还没有学习 Cut。如果有人能指出我正确的方向,我将不胜感激。我已经为此工作了将近一天。

我仍在尝试调整这种编程范式中的逻辑和声明式思维。如果您能与我分享,我将不胜感激,您个人是如何掌握逻辑编程的?

【问题讨论】:

  • 您的第二个 drop 谓词在结果的尾部有 _,这不好。这意味着您的结果将具有该匿名变量。所以你显然不是那个意思。将这些视为规则并尝试将其视为规则。 drop([X|Xs], Y, [X|_]) :-...删除了 Y-th 元素的列表 [X|Xs][X|_] if...。是什么让它成为现实?当然你需要Y > 1,但是[X|_]需要一个真正的尾巴,尾巴的值将由你的规则决定。
  • indexOf([X|Xs], X, A) 的第一个结果A 不是总是A = 1 吗? indexOf 策略的一个谬误是,在你到达那里之前,你并不真正知道 Y-th 元素将是什么,所以你没有它来获取它的索引。你真的只想要一个简单的计数器。
  • 感谢您的回复!今天早上我再次尝试重新尝试时看到了这一点。我正在查看 gtrace,但无法理解为什么我的结果会像 [a | _G264]。感谢您的第一条评论,我现在终于明白了。我不应该在我的结果中使用匿名变量,因为我需要它来实例化/存储我的结果。
  • 是的!今天早上我注意到了使用 indexOf 策略的谬误,我尝试使用累加器作为简单的计数器,但无法使其正常工作。不太确定如何在 Prolog 中创建计数器,因为在 Java 中我所要做的就是创建一个局部变量 i。我现在要在下面阅读您的长篇回复。

标签: list recursion prolog


【解决方案1】:

首先,看看您的方法,使用indexOf/3 存在缺陷。也就是说,在给定时间点,当您需要知道要删除的内容的索引时,您不知道该项目是什么,直到您找到它。此时,索引为1

这是以下规则的一个问题:

drop([X|Xs], Y, [X|_]) :-
    indexOf([X|Xs], X, A),
    Z is A mod Y,
    Z \== 0.

第一个子查询:indexOf([X|Xs], X, A) 将在第一次尝试时成功,A = 1,只是根据定义(当然,X 在列表1 中有索引1。当它成功时,然后下一行Z is A mod Y 产生 1,因为如果 Y > 01 mod Y 总是 1。因此,Z \== 0 在这种情况下总是会成功。

因此,您会得到结果:[X|_] 其中X 是列表的第一个元素。所以你得到的第一个解决方案,比如drop([1,2,3,4], 2, L).L = [1|_]。您的第三个 drop/3 谓词子句只是递归到列表中的下一个元素,因此它将以同样的方式继承第二个子句,产生 L = [2|_],依此类推...

从头开始,下面是一种思考此类问题的方法。

辅助谓词

我知道我想删除每个N-th 元素,所以有一个计数器很有帮助,这样每次它到达N 我都会忽略那个元素。这是通过辅助谓词drop/4 完成的,除了原始的N 之外,它还有一个循环计数器:

drop(L, N, R) :-
    drop(L, N, 1, R).   % Start counter at 1

基本规则

如果我从空列表中删除任何元素,我会得到空列表。我丢弃什么元素并不重要。表示为:

drop([], _, _, []).

您已经正确表达了此规则。以上是 4 参数版本。

递归规则 1 - N-th 元素

我有列表[X|Xs]XN-th 元素索引,那么如果我跳过X,结果是R,将我的索引计数器重置为1,并且从Xs 中删除N-th 元素

drop([_|Xs], N, N, R) :-   % I don't care what the element is; I drop it
    drop(Xs, N, 1, R).

递归规则 2 - 除了N-th 元素

我有列表[X|Xs]XA-th 元素(N),那么如果我增加我的索引计数器(A),结果是[X|R],并使用我更新的索引计数器从Xs 中删除N-th 元素

drop([X|Xs], N, A, [X|R]) :-
    A < N,
    NextA is A + 1,
    drop(Xs, N, NextA, R).

这些都是需要的规则(其中 4 条)。

【讨论】:

  • 这正是我所需要的。 :) 感谢您向我介绍了辅助谓词的概念,并清楚地逐步解释了您如何看待这个问题。我从来不知道你可以这样。我有一个关于递归规则顺序的快速问题。在这个特定的问题中,我注意到如果我交换递归规则,它仍然解决了由于 Prolog 每次调用 drop 时从上到下搜索的问题。我想问一下是否有任何场景/示例必须将某些递归规则放在另一个之前?
  • @t22000 规则的顺序有时会产生影响。这取决于规则。在这种情况下,两个递归规则是互斥的,一个覆盖计数器为N 的情况,另一个覆盖计数器为&lt; N 的情况。但是,一般来说,您希望将特定(例如,基本情况)规则放在首位。
  • 非常感谢您的解释。在您的帮助下,我在 Prolog 上取得了很大进步,虽然还不是很好,并设法完成了我的其他任务。 :D 我想给你投票,但我的声誉还没有上升:(
  • @t22000 不用担心。我很高兴它帮助您学习“在 Prolog 中思考”。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-18
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多