【问题标题】:Nested loops with accumulators in B-PrologB-Prolog 中带有累加器的嵌套循环
【发布时间】:2014-01-22 17:57:02
【问题描述】:

B-Prolog 有逻辑循环。例如,我们可以这样计算 [1,2,3] 的总和:

test1 :-
    foreach(A in 1..3, [], ac(Sa, 0), (
            Sa^1 is Sa^0 + A
        )),
    writeln(sa(Sa)).

?- test1.
test1.
sa(6)
yes

但是当我尝试两个带有累加器的嵌套循环时,我得到了错误:

test2 :-
    foreach(_A in 1..3, [Sb], ac(Sa, 0), (
            foreach(B in 1..3, [], ac(Sb, 0), (
                    Sb^1 is Sb^0 + B
                )),
            writeln(sb(Sb)),
            Sa^1 is Sa^0 + Sb
        )),
     writeln(sa(Sa)). 

?- test2.
test2.
*** error(invalid_argument,(+)/2)

另一种变体,外循环的局部变量列表中不包括Sb:

test3 :-
    foreach(_A in 1..3, [], ac(Sa, 0), (
            foreach(B in 1..3, [], ac(Sb, 0), (
                    Sb^1 is Sb^0 + B
                )),
            writeln(sb(Sb)),
            Sa^1 is Sa^0 + Sb
        )),
    writeln(sa(Sa)). 

?- test3.
test3.
sb(6)
*** error(invalid_argument,(+)/2)

在 B-Prolog 中甚至可以使用累加器嵌套循环吗?

我的 B-Prolog 版本是 8.0#1。

【问题讨论】:

  • 在你的真实代码中,内循环的目标是依赖外循环的变量还是累加器?在此处显示的代码中,Sb 是常量,可以预先计算。我猜你的真实代码中不是这种情况?
  • 是的,我只是提供了非常简单的废话代码来显示带有累加器的嵌套循环的问题。
  • 是的,我也很怀疑。 :)
  • 请注意(^)/2Sb^1 is Sb^0 + B 中的使用与ISO 可评估函子(^)/2 发生冲突。见complang.tuwien.ac.at/ulrich/iso-prolog/dtc2#pow

标签: prolog b-prolog


【解决方案1】:

您的程序在编译后可以正常运行。

| ?- cl(sergey)

Compiling::sergey.pl
** Warning: Variable 'Sb' is treated as global in foreach (17-25).
** Warning: Variable 'Sb' is treated as global in list_comprehension (36-38).
compiled in 0 milliseconds
loading...

yes
| ?- test1
sa(6)

yes
| ?- test2
sb(6)  
sb(6)
sb(6)
sa(18)

yes
| ?- test3
sb(6)
sb(6)
sb(6)
sa(18)

解释器一定有问题。这个蓄能器的东西非常丑陋,我从不使用它。在 B-Prolog 的继承者 Picat 中,您可以使用 := 来“更新”变量。

    test1 =>
        Sa = 0,
        foreach(A in 1..3)
            Sa := Sa+A
        end,
        writeln($sa(Sa)).

    test2 =>
        Sa = 0,
        foreach(_A in 1..3)
            Sb := 0,
            foreach(B in 1..3)
               Sb := Sb+B
            end,
            writeln($sb(Sb)),
            Sa := Sa+Sb
        end,
        writeln($sa(Sa)). 

更好的方法是使用列表推导。

    test1 =>
        Sa = sum([A : A in 1..3]),
        writeln($sa(Sa)).

    test2 =>
        Sa = sum([Sb : _A in 1..3, Sb=sum([B : B in 1..3])]),
        writeln($sa(Sa)). 

编译器将总和编译成使用 ':=' 的程序。由于实际上并未构造列表,因此没有开销。

【讨论】:

    【解决方案2】:

    http://www.probp.com/manual/node55.html 似乎显示了一个嵌套循环,扁平化:

    ?-foreach(A in [a,b], I in 1..2, ac(L,[]), L^1=[(A,I)|L^0]).
    L = [(b,2),(b,1),(a,2),(a,1)]
    

    我们只需要恢复内循环结束条件。看看这是否有效:

    test2 :-
        foreach(A in 1..3,  
                B in 1..3, [], [ac(Sa, 0), ac(La, []) ac(Sb, 0)], (
                        (  ( La^0 = [] ; La^0 = [A] )  % or just (La^0 = [A])
                        ->                             % same A, next B
                           Sa^1 is Sa^0, Sb^1 is Sb^0 + B
                        ;                              % new A, B-loop has ended
                           Sa^1 is Sa^0 + Sb^0, writeln(sb(Sb^0)), Sb^1 is 0 + B
                        ),
                        La^1 = [A]
                    )),
         Sa_final = Sa + Sb,                           % process the final iteration
         writeln(sa(Sa_final)). 
    

    如果As 中可能有重复,只需按照文档所示将它们编入索引:

    ?-foreach((A,I) in ([a,b],1..2), ac(L,[]), L^1=[(A,I)|L^0]).
    L = [(b,2),(a,1)]
    

    并打破 I 中的更改。

    (未测试)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-21
      • 1970-01-01
      相关资源
      最近更新 更多