recursion - Prolog 将分钟转换为小时

标签 recursion prolog

这是我创建的代码。

mins_to_hours(In, H, M):-
  In < 60,
  H = 0,
  M is In.
mins_to_hours(In, H, M):-
  In >= 60,
  H is H1+1,
  In1 is In-60,
  mins_to_hours(In1, H1, M).

当分钟小于 60 时,它工作正常,例如

?- mins_to_hours(20,H,M).
H = 0,
M = 20 ;
false.

但是当尝试运行超过 60 分钟时

?- mins_to_hours(80,H,M).

它输出一个异常

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [9] _3198 is _3204+1
ERROR:    [8] mins_to_hours(80,_3232,_3234) at c:/.../xyz.pl:11
ERROR:    [7] <user>

在位置 H 是 H1+1,

有什么解决办法吗?

最佳答案

这是您的代码的更正版本。

mins_to_hours(Minutes_in, H, M) :-
    mins_to_hours_helper(0, Minutes_in, H, M).

mins_to_hours_helper(H0, M0, H0, M0):-
  M0 < 60, !.
mins_to_hours_helper(H0, M0, H, M):-
  M0 >= 60,
  H1 is H0+1,
  M1 is M0-60,
  mins_to_hours_helper(H1, M1, H, M).

主要变化是:

  1. 为了避免错误消息(参数未充分实例化),因为您的代码是递归的,它需要单独的传入和传出变量,即 H0H1,以及 M0M1
  2. 为了能够使用额外的变量,需要添加一个辅助谓词,即 mins_to_hours_helper
  3. 辅助谓词中的分钟和起始分钟变量实际上是相同的。
  4. 作为递归代码创建选择点,但答案应该是确定性的。这是在基本情况下通过削减 (!) 解决的。

以下是一些测试用例(使用 SWI-Prolog):

:- begin_tests(mins_to_hours).

test(-1) :-
    mins_to_hours(-1,H,M),
    assertion(H == 0),
    assertion(M == -1).

test(0) :-
    mins_to_hours(0,H,M),
    assertion(H == 0),
    assertion(M == 0).

test(1) :-
    mins_to_hours(1,H,M),
    assertion(H == 0),
    assertion(M == 1).

test(59) :-
    mins_to_hours(59,H,M),
    assertion(H == 0),
    assertion(M == 59).

test(60) :-
    mins_to_hours(60,H,M),
    assertion(H == 1),
    assertion(M == 0).

test(600) :-
    mins_to_hours(600,H,M),
    assertion(H == 10),
    assertion(M == 0).

test(601) :-
    mins_to_hours(601,H,M),
    assertion(H == 10),
    assertion(M == 1).

:- end_tests(mins_to_hours).

运行测试用例:

?- run_tests.
% PL-Unit: mins_to_hours ....... done
% All 7 tests passed
true.

注意:run_tests. 不适用于 SWISH ,

No permission to call sandboxed `'$current_module'(_4002,_4004)'

因此您必须手动输入每个查询并手动检查结果。

参见:sandbox.pl


现在有更好的方法来做到这一点。

mins_to_hours(Minutes_in, H, M) :-
    H is Minutes_in // 60,
    M is Minutes_in rem 60.

请注意,这是确定性的,不是递归的,并且通过了所有测试用例。

参见:f-///2 (整数除法)和rem/2 (整数除法的余数)


注意。

由于您没有指定当分钟数为负数时应该发生什么,但确实提供了将分钟数小于 60 并移至结果的代码,因此此代码重现了该响应。

代码的一个变体是使用 mod/2而不是 rem/2。这将根据输入给出不同的答案,但可能是所需的结果。


根据@false 的反馈更新。

当编写的代码超出简单练习时,需要使用 modes 编写记在心里。

原答案代码写成

声明:mins_to_hours(++Minutes_in:int, -H:int, -M:int) 已确定。

意思是

Minutes_in 必须绑定(bind)到一个整数
H 必须是一个变量
M 必须是一个变量

但是正如@false 指出的那样

?- mins_to_hours(Total, H, 1), Total = 61, H = 1.
false.

Declaration: mins_to_hours(-Minutes_in:int, -H:int, +M:int) is det.

?- Total = 61, H = 1, mins_to_hours(Total, H, 1).
Total = 61,
H = 1.

Declaration: mins_to_hours(+Minutes_in:int, +H:int, +M:int) is det.

第一个示例返回 false,(失败)但应该返回 true, 第二个示例针对相同的值但以不同的模式返回有效答案。

虽然我的回答只适用于一种模式 answer Daniel Lyons 与其他人合作,因为它使用约束。

?- mins_to_hours(Total, H, 1), Total = 61, H = 1.
Total = 61,
H = 1.

因此,为了避免@false 的第一个示例返回实际上是错误的false,它应该抛出一个Arguments are not enoughfully instantiated 错误。 @false 还指出最简单的方法是

delay unification after the cut

这是更新后的代码:

mins_to_hours(Minutes_in, H, M) :-
    mins_to_hours_helper(0, Minutes_in, H, M).

mins_to_hours_helper(H0, M0, H1, M1):-
  M0 < 60, !,
  H0 = H1,
  M0 = M1.
mins_to_hours_helper(H0, M0, H, M):-
  M0 >= 60,
  H1 is H0+1,
  M1 is M0-60,
  mins_to_hours_helper(H1, M1, H, M).

哪些通过了测试用例

?- run_tests.
% PL-Unit: mins_to_hours ....... done
% All 7 tests passed
true.

并给出第一个例子的错误:

?- mins_to_hours(Total, H, 1), Total = 61, H = 1.
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [11] _6584<60
ERROR:   [10] mins_to_hours_helper(0,_6612,_6614,1) at c:/XYZ.pl:23
ERROR:    [8] '<meta-call>'(user:(...,...)) <foreign>
ERROR:    [7] <user>
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

关于recursion - Prolog 将分钟转换为小时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54688256/

相关文章:

java - 如何使用递归函数代替嵌套循环?

list - 有人可以用我的 Haskell 代码诊断问题吗?

prolog - 计算机功能必须满足哪些要求才能被视为 "monotonic"?

Prolog: 未捕获异常: error(existence_error(procedure,s/3),top_level/0)

prolog - 序言组合练习

prolog - 仅将bagof/3用于副作用

c# - 如果我们需要使用索引作为条件,如何将循环修改为递归?

Symfony2 : Recursive Validation

ios - 具有完成 block 的递归方法

list - Prolog:findall 但只保存一些解决方案