此答案直接跟进this previous answer,特别是@WillNess 的评论建议“[...] 切换两个目标,因此在失败时尽快停止遍历 [...] 在phrase 之前有chain [...]"。
lazy_chain/2 类似于chain/2,但利用prolog-coroutining 等待足够的实例化:
:-
use_module(
library(clpfd))。
懒惰链(Zs,R_2):-
(
var(R_2) ->
instantiation_error(R_2)
; clpfd:chain_relation(R_2) ->
freeze(Zs,lazy_chain_aux(Zs,R_2))
;否则 ->
domain_error(chain_relation, R_2)
)。
懒惰链辅助([],_)。
懒惰链辅助([Z0|Zs],R_2):-
冻结(Zs,lazy_chain_aux_(Zs,R_2,Z0))。
惰性链_辅助_([],_,_)。
lazy_chain_aux_([Z1|Zs], R_2, Z0) :-
呼叫(R_2,Z0,Z1),
冻结(Zs,lazy_chain_aux_(Zs,R_2,Z1))。
基于lazy_chain/2,我们这样定义is_bintreeL/2:
is_bintreeL(T) :-
懒惰链(Zs,#phrase(in_order(T), Zs)。
那么……“早期失败”呢?
?- T = node(
2, nil, node(
1, nil, node(3, nil, node(4, nil, node(5, nil, node( 6,无,节点(7,无,节点(8,无,节点(9,无,节点(10,无,节点(11,无,节点(12,无,节点(13,无,节点(14,无,节点(15,无,节点(16,无,节点(17,无,节点(18,无,节点(19,无,节点(20,无,节点(21,无,节点(22,无,节点(23,无,节点(24,无,节点(25,无,节点(26,无,节点(27,无,节点(28,无,节点(29,无,节点(30,无,节点( 31,无,节点(32,无,节点(33,无,节点(34,无,节点(35,无,节点(36,无,节点(37,无,节点(38,无,节点(39,无,节点(40,无,节点(41,无,节点(42,无,节点(43,无,节点(44,无,节点(45,无,节点(46,无,节点(47,无,节点(48,无,节点(49,无,节点(50,无,节点(51,无,节点(52,无,节点(53,无,节点(54,无,节点(55,无,节点( 56,无,节点(57,无,节点(58,无,节点(59,无,节点(60,无,节点(61,无,节点(62,无,节点(63,无,节点(64,无,节点(65,无,节点(66,无,节点(67,无,节点(68,无,节点(69,无,节点(70,无,节点(71,无,节点(72,无,节点(73,无,节点(74,无,节点(75,无,节点(76,无,节点(77,无,节点(78,无,节点(79,无,节点(80,无,节点(81,无,节点(82,无,节点(83,无,节点(84,无,节点(85,无,节点(86,无,节点(87,无,节点( 88,无,节点(89,无,节点(90,无,节点(91,无,节点(92,无,节点(93,无,节点(94,无,节点(95,无,节点(96,无,节点(97,无,节点(98,无,节点(99,无,节点(100,无,无))))))))))))))))))))))) )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))),
时间((短语(in_order(T),Zs),
渴望_chain(Zs,#210 次推断,0.000 CPU 在 0.000 秒内(98% CPU,4100201 唇)
错误的。
?- T = node(
2, nil, node(
1, nil, node(3, nil, node(4, nil, node(5, nil, node( 6,无,节点(7,无,节点(8,无,节点(9,无,节点(10,无,节点(11,无,节点(12,无,节点(13,无,节点(14,无,节点(15,无,节点(16,无,节点(17,无,节点(18,无,节点(19,无,节点(20,无,节点(21,无,节点(22,无,节点(23,无,节点(24,无,节点(25,无,节点(26,无,节点(27,无,节点(28,无,节点(29,无,节点(30,无,节点( 31,无,节点(32,无,节点(33,无,节点(34,无,节点(35,无,节点(36,无,节点(37,无,节点(38,无,节点(39,无,节点(40,无,节点(41,无,节点(42,无,节点(43,无,节点(44,无,节点(45,无,节点(46,无,节点(47,无,节点(48,无,节点(49,无,节点(50,无,节点(51,无,节点(52,无,节点(53,无,节点(54,无,节点(55,无,节点( 56,无,节点(57,无,节点(58,无,节点(59,无,节点(60,无,节点(61,无,节点(62,无,节点(63,无,节点(64,无,节点(65,无,节点(66,无,节点(67,无,节点(68,无,节点(69,无,节点(70,无,节点(71,无,节点(72,无,节点(73,无,节点(74,无,节点(75,无,节点(76,无,节点(77,无,节点(78,无,节点(79,无,节点(80,无,节点(81,无,节点(82,无,节点(83,无,节点(84,无,节点(85,无,节点(86,无,节点(87,无,节点( 88,无,节点(89,无,节点(90,无,节点(91,无,节点(92,无,节点(93,无,节点(94,无,节点(95,无,节点(96,无,节点(97,无,节点(98,无,节点(99,无,节点(100,无,无))))))))))))))))))))))) )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))),
time((
lazy_chain(Zs,#52 次推断,0.000 CPU 在 0.000 秒内(98% CPU,1225664 唇)
错误的。
懒惰获胜——至少在上述情况下:)
但是请注意,将lazy_chain/2 与dcg 一起使用可能会导致难以找到的错误!
如需更强大的解决方案,请参阅this alternative answer...
为了完整起见,这里是eager_chain/2的源代码:
急切链(Zs,R_2):-
( var(R_2) -> 实例化_error(R_2)
; clpfd:chain_relation(R_2) -> eager_chain_aux(Zs, R_2)
;否则-> domain_error(chain_relation, R_2)
)。
急切链辅助([],_)。
eager_chain_aux([Z0|Zs], R_2) :-
eager_chain_aux_(Zs, R_2, Z0)。
eager_chain_aux_([], _, _)。
eager_chain_aux_([Z1|Zs], R_2, Z0) :-
呼叫(R_2,Z0,Z1),
eager_chain_aux_(Zs, R_2, Z1)。