Prolog 中带有“true”值的提前停止谓词

问题描述 投票:0回答:2

我正在 Prolog 中使用

http_server
编写一个
http_session
应用程序。请求使用
dicontiguous
(多个定义)谓词
app
进行处理,该谓词不得失败(否则会向客户端显示 500 错误)。我需要以某种方式提前退出谓词,以便其余部分不会运行,但会显示一条自定义消息,并且不会失败:

:- discontiguous app/1.

app(Request) :-
    ensure_path(Request), !, % I want a `!` after I determined the path is valid, so no other `app` is ran

    % grab session id as `Session`
    http_session_id(Session),
    
    % try to get session data, fail otherwise (I check something else in my code, but `http_session_data` should suffice for this example)
    (http_session_data(my_data(Data), Session); show_error, ...), % some syntactic magic here...
    
    % render something if session data has value
    render_some_success_html(Data).

这是我正在尝试做的一段非 Prolog 语言的伪代码(不要责骂我比较不同范式的语言):

function app(request) {
    if (!ensure_path(request)) ...; // try other app(request)
    let session = http_session_id();
    let data = http_session_data('my_data', session);
    if (!data) {
        show_error();
        return true; // this is important
    }
    render_some_success_html(data);
}

我尝试使用

!, fail
代替
...
,但这失败了(swipl 说
goal unexpectedly failed
),我需要一个
true
来代替。

编辑:谓词中可能有多个这样的早期返回,每个都有自己的

show_error
变体。

prolog swi-prolog early-return
2个回答
0
投票

SWI-Prolog 最近添加的一个支持更“函数式”编程风格的功能是:

predicate_head(Args...) :- Body

你可以这样做:

predicate(Args), Condition1 => Body1.
predicate(Args), Condition2 => Body2.
...
predicate(Args) => Default.

从上到下评估;它承诺第一个适合的条件;如果底部没有默认值并且没有一个条件为真,那么它在运行时会失败。

所有这些都记录在这里:https://www.swi-prolog.org/pldoc/man?section=ssu

请尝试通读它,因为它展示了如何在没有它的情况下编写 Prolog 程序,以及如何使用它编写相同的程序。我强烈怀疑它会对您当前的问题有所帮助。


0
投票

早期返回的目的是让代码不要太嵌套,并保持自上而下,失败操作靠近这些检查,使其更具可读性。

感谢@TA_intern的建议,我决定在每个故障点上使用该

predicate(Args)
的两种变体。以下代码将使用
!
变体,但读者也可以尝试新的“Picat”语法。

所以这是我的解决方案:

  1. 针对此类故障点将谓词拆分为多个谓词。

    给出一个依赖调用链的简单示例,每个调用都会为链中的下一位提供一些结果以供使用:

    predicate_with_failure_points(In1) :-
        failure_point1(In1, Out1),  % want to `show_error1` else on fail
        failure_point2(Out1, Out2), % and `show_error2` here
        success(Out2).
    

    拆分看起来像:

    predicate_with_failure_point1(In1) :-
        failure_point1(In1, Out1), !, % `!` is not strictly necessary, but it might save a lot of headaches, might be necessary when dealing with side-effects 
        predicate_with_failure_point2(Out1).
    
    predicate_with_failure_point2(In2) :-
        failure_point2(In2, Out2), !,
        success(Out2). % could split this bit here, especially if `success` is not a single line of code but quite large and would put `show_error2` too far down. let's assume this `success` is already a split off part.
    
  2. 为每个谓词提供一个替代变体作为该故障点的处理程序。

    predicate_with_failure_point1(In1) :-
        failure_point1(In1, Out1), !,
        predicate_with_failure_point2(Out1).
    predicate_with_failure_point1(In1) :- % will execute if `!` was not reached
        show_error1(In1).
    
    predicate_with_failure_point2(In2) :-
        failure_point2(In2, Out2), !,
        success(Out2).
    predicate_with_failure_point2(In2) :-
        show_error2(In2).
    

生成的代码:

  • 失败时不继续执行其余(原始)代码;
  • 从上到下,没有不必要的嵌套或翻转代码部分;
  • 在故障点旁边放置一个
    show_error

原始问题中给出的示例将如下所示:

:- discontiguous app/1.

app(Request) :-
    ensure_path(Request), !,
    http_session_id(Session),
    check_session_data(Session).

check_session_data(Session) :-
    http_session_data(my_data(Data), Session), !,
    render_some_success_html(Data).
check_session_data(_Session) :-
    show_error.
© www.soinside.com 2019 - 2024. All rights reserved.