强制变量重新分配

强制变量重新分配

本文介绍了强制变量重新分配(Prolog)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作业是接收两个变量,一个介于 0 和 10,000 之间的数字,以及一个表示 1 和该数字之间有多少个圆形素数的数字.

The homework is to take in two variables, a number between 0 and 10,000 and a number of how many circular primes there are between 1 and that number.

我在通过递归将变量向上传递时遇到了麻烦(我认为回溯就是它的名字.)我得到了正确的数字,而且我很确定我已经理解了这个概念,我遇到的问题是当我尝试重新分配一个变量时它会抛出一个错误 (?!)

I am having trouble passing the variable back up through the recursion (backtracing is what it is called, I think.) I get the right number and I am pretty sure I have the concept down, the problem I am having is that it throws an error when I try to reassign a variable (?!)

代码如下:

circPrimeCompare(Below, NumCirc):-
    RealNum is 0,
    circPrimeCompare(1, Below, RealNum, []),
    print('R: '),
    print(RealNum),
    nl,
    (NumCirc =:= RealNum).

circPrimeCompare(_, 0, _, _).
circPrimeCompare(N, 1, _, _):- prime(N), print('aaa').
circPrimeCompare(X, Below, RealNum, L):-
    ( prime(X), X<Below ->
        print(X),
        nl,
        numDigits(X, Y),
        rotate(X, Y, N2),
        (   prime(N2)
            ->  RealNum2 is RealNum + 1
            ;   RealNum2 is RealNum
            ),
        X2 is X + 1,
        (   not(circPrimeCompare(X2, Below, RealNum2, L))
            ->  RealNum = RealNum2, print(RealNum), nl
            ;   RealNum = RealNum2, print(RealNum), nl
            ),
        print('RealNum2: '),
        print(RealNum),
        nl
    ;
    ( X<Below ->
        X2 is X + 1,
        RealNumPass is RealNum,
        (   not(circPrimeCompare(X2, Below, RealNumPass, L))
            ->  RealNum = RealNumPass, print(RealNum), nl
            ;   RealNum = RealNumPass, print(RealNum), nl
            ),
        print('RealNum: '),
        print(RealNum),
        nl
        )
    ).

这是跟踪:

   Fail: (26) circPrimeCompare(10, 10, 4, []) ? creep
^  Exit: (25) not(user:circPrimeCompare(10, 10, 4, [])) ? creep
   Call: (25) 4=4 ? creep
   Exit: (25) 4=4 ? creep
...
   Exit: (24) circPrimeCompare(9, 10, 4, []) ? creep
^  Fail: (23) not(user:circPrimeCompare(9, 10, 4, [])) ? creep
   Call: (23) 4=4 ? creep
   Exit: (23) 4=4 ? creep
...
   Exit: (22) circPrimeCompare(8, 10, 4, []) ? creep
^  Fail: (21) not(user:circPrimeCompare(8, 10, 4, [])) ? creep
   **Call: (21) 3=4 ? creep
   Fail: (21) 3=4 ? creep**
   Redo: (21) numDigits(7, _G589) ? creep

粗体部分是什么让我失望.我真的不明白为什么它会这样.是因为变量本质上只是一种用途吗?关于如何修复它的任何想法?

The bolded part is what is throwing me. I don't really get why it is acting this way. Is it because variables are essentially one use only? Any ideas on how to fix it?

(是的,我意识到这是非常非常糟糕的代码.在此作业之前我从未在 Prolog 中写过任何东西.)

(And yes, I realize this is really, really terrible code. I had never written anything in Prolog before this assignment.)

推荐答案

您没有以Prolog"的方式思考问题.特别是尝试重新分配"一个变量不是 Prolog 的方式.变量在满足目标和子目标的过程中被绑定,而不是重新分配一个值,我们经常安排代码携带一个累加器"变量,只在计算的最后阶段将其值传递给另一个最终"变量.

You are not thinking about the problem in a "Prolog" way. In particular trying to "reassign" a variable is not the Prolog way. Variables are bound along the way of satisfying goals and subgoals, and instead of reassigning a value, we often arrange the code to carry an "accumulator" variable, only to pass its value to another "final" variable in the last stage of a computation.

回溯和递归是不同的东西.当目标(或子目标)失败时发生回溯,Prolog引擎"试图以不同的方式满足该目标.如果我们用完不同的规则等来满足目标,那么它就会失败.(这可能会导致 Prolog 引擎回溯到之前的子目标并尝试以不同的方式满足那个.)

Backtracking and recursion are different things. Backtracking occurs when a goal (or subgoal) fails, and the Prolog "engine" tries to satisfy that goal in a different way. If we run out of different rules, etc. to satisfy the goal, then it fails. (Which may cause the Prolog engine to backtrack into a previous subgoal and try to satisfy that in a different way.)

递归是指根据自身定义谓词,即如果调用该谓词会导致作为子目标再次调用相同的谓词.

Recursion is when a predicate is defined in terms of itself, i.e. if invoking the predicate can cause the same predicate to be invoked again as a subgoal.

要在 Prolog 中完成许多任务,我们必须根据递归而不是迭代来考虑它们,这在过程语言中是很自然的.这就是我们求助于累加器"变量的原因,在不习惯的人眼中,这些变量似乎是谓词中额外的、可能是不必要的参数.

To do many tasks in Prolog we have to think about them in terms of recursion rather than iteration, as would be natural in a procedural language. This is why we resort to "accumulator" variables, which appear to the unaccustomed eye as extra and possibly unnecessary arguments in a predicate.

然而,一旦你看到这个想法付诸实施,你可能会因为能够以新的方式应用这个概念而获得某种程度的满足感.

However once you see the idea in action, you may get some measure of satisfaction in being able to apply the concept in new ways.

让我们将给定列表中的数字相加作为一个模型问题.天真的方法是这样写:

Let's take as a model problem adding up the numbers in a given list. The naive approach would be to write:

sum(List,Sum).

其中 List 是一个数字列表,Sum 应该是我们的输出,在列表中保存规定的值总和.

where the List is a list of numbers and Sum should be our output, holding the prescribed sum of values in the list.

基本思想很自然,你考虑列表的 HeadTail,如果列表不是(还)空,你想重新分配" Sum = Sum + Head,然后递归地处理Tail(直到列表为空并且我们有我们想要的Sum).

The basic idea is pretty natural, you consider the Head and Tail of the list, and if the list is not (yet) empty, you want to "reassign" Sum = Sum + Head, then proceed recursively to tackle the Tail (until the list is empty and we have the Sum we wanted).

但这在 Prolog 中没有意义,因为您不能以过程语言允许的相同方式更改 Sum 的值.这是将累加器参数添加到我们的谓词并执行相同的工作的地方,但以 Prolog 喜欢的方式.

But this doesn't make sense in Prolog, because you cannot change the value of Sum in the same way a procedural language allows. This is where an accumulator argument gets added to our predicate and does the same work, but in a way that Prolog likes.

sumAux([ ],Sum,Sum).
sumAux([H|T],Accum,Sum) :-
    NewAccum is Accum + H,
    sumAux(T,NewAccum,Sum).

sum(List,Sum) :- sumAux(List,0,Sum).

这里我们引入了一个带有额外参数的辅助"谓词 sumAux/3,并且我们通过设置根据该谓词定义了 sum/2累加器"中间参数为零,但同时传递 ListSum 参数.

Here we've introduced an "auxillary" predicate sumAux/3 with an extra argument, and we've defined sum/2 in terms of that predicate by setting the "accumulator" middle argument to zero, but passing the List and Sum arguments along.

稍微思考一下这是如何工作的,您的努力就会得到回报,很快我预计您会以各种新颖的方式应用这种范式(例如计算圆形素数).

A little thinking about how this works will repay your efforts, and soon I expect you'll be applying this paradigm in all sorts of novel ways (such as counting circular primes).

这篇关于强制变量重新分配(Prolog)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 08:03