微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在两个方向上进行时间解析谓词工作?

如何解决如何在两个方向上进行时间解析谓词工作?

使用SWI-Prolog,我做了一个简单的谓词,将hh:mm格式的时间与时间项相关联。

time_string(time(H,M),String) :-
    number_string(H,Hour),number_string(M,Min),string_concat(Hour,":",S),string_concat(S,Min,String).

尽管谓词只能在一个方向上起作用。

time_string(time(10,30),String).
String = "10:30".      % This is perfect.

很遗憾,此查询失败。

time_string(Time,"10:30").
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [11] number_string(_8690,_8692)
ERROR:   [10] time_string(time(_8722,_8724),"10:30") at /tmp/prolcompDJBcEE.pl:74
ERROR:    [9] toplevel_call(user:user: ...) at /usr/local/logic/lib/swipl/boot/toplevel.pl:1107

如果我不必编写全新的谓词来回答此查询,那将是非常不错的。有办法吗?

解决方法

好吧,从结构化术语time(H,M)到字符串String 比从非结构化String到术语time(H,M)容易。

您的谓词沿“世代”方向工作。

对于另一个方向,您想解析 String。在这种情况下,这在计算上很容易,并且无需搜索/回溯就可以完成,这很不错!

使用Prolog的"Definite Clause Grammar"语法“只是”一种编写处理“事物列表”的谓词的好方法。在这种情况下,填充列表是字符列表(长度为1的原子)。 (有关SWI-Prolog中的相关页面,请参见here

如果运气好的话,DCG代码可以向后/向前运行,但是通常不是这样。满足某些效率或因果要求的实际代码可能会强制执行该代码,因此您可以在单个谓词的掩盖下首先按“处理方向”进行分支,然后遍历完全不同的代码结构来交付货物。

所以在这里。代码立即“衰减”到解析并生成分支。 Prolog尚未设法完全基于约束。您只是必须先做一些事情。

无论如何,让我们这样做:

:- use_module(library(dcg/basics)).

% ---
% "Generate" direction; note that String may be bound to something
% in which case this clause also verifies whether generating "HH:MM"
% from time(H,M) indeed yields (whatever is denoted by) String.
% ---

process_time(time(H,M),String) :-
   integer(H),% Demand that H,M are valid integers inside limits
   integer(M),between(0,23,H),59,!,% Guard passed,commit to this code branch
   phrase(time_g(H,Chars,[]),% Build Codes from time/2 Term
   string_chars(String,Chars).            % Merge Codes into a string,unify with String

% ---
% "Parse" direction. 
% ---

process_time(time(H,String) :-
   string(String),% Demand that String be a valid string; no demands on H,M  
   !,commit to this code branch
   string_chars(String,Chars),% Explode String into characters
   phrase(time_p(H,[]).          % Parse "Codes" into H and M

% ---
% "Generate" DCG
% ---
   
time_g(H,M) --> hour_g(H),[':'],minute_g(M).
hour_g(H)   --> { divmod(H,10,V1,V2),digit_int(D1,V1),digit_int(D2,V2) },digit(D1),digit(D2).
minute_g(M) --> { divmod(M,digit(D2).

% ---
% "Parse" DCG
% ---
   
time_p(H,M) --> hour_p(H),minute_p(M).
hour_p(H)   --> digit(D1),digit(D2),{ digit_int(D1,H is V1*10+V2,H) }.
minute_p(M) --> digit(D1),M is V1*10+V2,M) }.   
   
% ---
% Do I really have to code this? Oh well!
% ---

digit_int('0',0).
digit_int('1',1).
digit_int('2',2).
digit_int('3',3).
digit_int('4',4).
digit_int('5',5).
digit_int('6',6).
digit_int('7',7).
digit_int('8',8).
digit_int('9',9).

% ---
% Let's add plunit tests!
% ---

:- begin_tests(hhmm).

test("parse 1",true(T == time(0,0)))   :- process_time(T,"00:00").
test("parse 2",true(T == time(12,13))) :- process_time(T,"12:13").
test("parse 1",true(T == time(23,59))) :- process_time(T,"23:59").
test("generate",true(S == "12:13"))     :- process_time(time(12,13),S).
test("verify",true)                   :- process_time(time(12,"12:13").
test("complete",true(H == 12))          :- process_time(time(H,"12:13").

test("bad parse",fail)                 :- process_time(_,"66:66").
test("bad generate",fail)                 :- process_time(time(66,66),_).

:- end_tests(hhmm).

很多代码。

行得通吗?

?- run_tests.
% PL-Unit: hhmm ........ done
% All 8 tests passed
true.
,

鉴于模式的简单性,DCG可能被视为过高的杀伤力,但实际上,它使我们能够轻松访问可输入某些声明性算术库的原子成分。例如

:- module(hh_mm_bi,[hh_mm_bi/2,hh_mm_bi//1
         ]).

:- use_module(library(dcg/basics)).
:- use_module(library(clpfd)).

hh_mm_bi(T,S) :- phrase(hh_mm_bi(T),S).

hh_mm_bi(time(H,M)) --> n2(H,23),":",n2(M,59).
n2(V,U)             --> d(A),d(B),{V#=A*10+B,V#>=0,V#=<U}.
d(V)                --> digit(D),{V#=D-0'0}.

一些测试

?- hh_mm_bi(T,`23:30`).
T = time(23,30).

?- hh_mm_bi(T,`24:30`).
false.

?- phrase(hh_mm_bi(T),S).
T = time(0,0),S = [48,48,58,48] ;
T = time(0,1),49] ;
...

编辑

library(clpfd)不是声明式算术的唯一选择。这是使用library(clpBNR)的另一张照片,但是它要求您使用?- pack_install(clpBNR).安装相应的软件包。完成此操作后,可以得到与上述功能等效的另一种解决方案

:- module(hh_mm_bnr,[hh_mm_bnr/2,hh_mm_bnr//1
         ]).

:- use_module(library(dcg/basics)).
:- use_module(library(clpBNR)).

hh_mm_bnr(T,S) :- phrase(hh_mm_bnr(T),S).

hh_mm_bnr(time(H,U)              --> d(A),{V::integer(0,U),{V==A*10+B}}.
d(V)                 --> digit(D),{{V==D-0'0}}.

编辑

@DavidTonhofer的评论(现已删除)使我认为可以使用更简单的方法,将“发电能力”移至d // 1:


:- module(hh_mm,[hh_mm/2,hh_mm//1
         ]).

hh_mm(T,S) :- phrase(hh_mm(T),S).

hh_mm(time(H,U)          --> d(A),{ V is A*10+B,V>=0,V=<U }.
d(V)             --> [C],{ member(V,[0,1,2,3,4,5,6,7,8,9]),C is V+0'0 }.
,
time_string(time(H,String)
:-
hour(H),minute(M),number_string(H,Hs),number_string(M,Ms),string_concat(Hs,S),string_concat(S,Ms,String)
.

hour(H) :- between(0,11,H) .

minute(M) :- between(0,M) .
/*
?- time_string(time(10,30),B).
B = "10:30".

?- time_string(time(H,"10:30").
H = 10,M = 30 ;
false.

?- time_string(time(H,S).
H = M,M = 0,S = "0:0" ;
H = 0,M = 1,S = "0:1" ;
H = 0,M = 2,S = "0:2" ;
H = 0,M = 3,S = "0:3" %etc.
*/
,

另一个答案,避免将DCG视为此任务的过分杀伤力。或更确切地说,这里涉及两个独立的任务:并非每个关系都可以在单个Prolog谓词中表达,尤其是并非每个关系都像SWI-Prolog的字符串一样具有逻辑性。

因此,这是其中一项任务的解决方案,它可以不时计算字符串(这是您的代码重命名):

time_string_(time(H,String) :-
    number_string(H,Hour),Min),string_concat(Hour,Min,String).

例如:

?- time_string_(time(11,59),String).
String = "11:59".

这是相反转换的简单实现:

string_time_(String,time(H,M)) :-
    split_string(String,"",[Hour,Minute]),Minute).

例如:

?- string_time_("11:59",Time).
Time = time(11,59).

这是一个谓词,它根据已知的参数来选择要使用哪些转换。确切的条件将取决于您的应用程序中可能发生的情况,但是可以合理地说,如果字符串确实是字符串,我们想尝试对其进行解析:

time_string(Time,String) :-
    (   string(String)
    ->  % Try to parse the existing string.
        string_time_(String,Time)
    ;   % Hope that Time is a valid time term.
        time_string_(Time,String) ).

这将同时翻译两种方式:

?- time_string(time(11,String).
String = "11:59".

?- time_string(Time,"11:59").
Time = time(11,59).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。