Bezdepbonusu7Программирование: статьи и ответы

Вопрос

DCG: присвоение остатка переменной в коде DCG {}

Автор вопроса: Emacs The VikingОтветов: 3

Этот ответ: Очень простой синтаксис пролога dcg немного помог мне, но [X] получает только следующий персонаж, я хочу всю энчиладу, читайте дальше!

Я использую GNU Prolog для написания синтаксического анализатора параметров команды, и я застрял в точке DCG. У меня есть это грамматическое правило, которое ищет, например, "foo --as=json", и я просто не могу понять, как получить в свои руки результат "чего угодно", код:

as_opt --> "--as=", anything, { c( as_opt )}, !.
anything --> [], {c(anything_match)}.

И расширение gprolog этого:

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B.

anything(A, B) :-
        c(anything_match), !,
        A = B.

Предикат "c()" прост и просто используется для отслеживания того, что правило выполняется с помощью format() в stdout, чтобы я мог видеть, что происходит во время его выполнения. Если бы я написал код вручную, я бы сделал:

%% for completeness here!
c(Msg) :- format("Processed ~w~n", [Msg]).

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B,
        { g_assign( gvValue, B )}. %% just for example

Возвращаясь к исходному DCG:

as_opt --> "--as=", anything, { c( as_opt ), gassign( gvValue, ??? )}, !.

Так что же там, где "???" является. Возможно ли... так должно быть. Я собираюсь еще раз перечитать правила gprolog о том, как он расширяет правила DCG, на случай, если я собираюсь (facepalm) сам, но тем временем любая помощь будет очень кстати.

Спасибо, Шон.

gnuprologdcg

Ответы

Ответ 1 · рейтинг 3 · mat

Вы просите один из простейших нетерминалов DCG, который описывает любой список:

list --> [].
list --> [_], list.

Чтобы фактически получить доступ к описываемому списку, вы вводите аргумент:

list([])    --> [].
list([L|Ls) --> [L], list(Ls)

Вы можете использовать его следующим образом:

as_opt(Option) --> "--as=", list(Option).

Ответ 2 · рейтинг 3 · Paulo Moura

Есть лучшее решение, чем то, которое описано в мате, которое быстрее и избегает ложных точек выбора, но требует поддержки встроенного нетерминала call//1, как это найдено, например. SWI-Prolog, GNU Prolog и другие компиляторы Prolog. Также на Logtalk. Учитывать:

as_opt(Option) --> "--as=", list(Option).

list([L|Ls]) --> [L], list(Ls).
list([]) --> [].

as_opt2(Option) --> "--as=", call(rest(Option)).

rest(Rest, Rest, _).

Использование SWI-Prolog для лучшей иллюстрации различий:

?- phrase(as_opt(Option), "--as=json").
Option = [106, 115, 111, 110] ;
false.

?- phrase(as_opt2(Option), "--as=json").
Option = [106, 115, 111, 110].

?- time(phrase(as_opt(Option), "--as=json")).
% 9 inferences, 0.000 CPU in 0.000 seconds (57% CPU, 562500 Lips)
Option = [106, 115, 111, 110] ;
% 2 inferences, 0.000 CPU in 0.000 seconds (63% CPU, 133333 Lips)
false.

?- time(phrase(as_opt2(Option), "--as=json")).
% 6 inferences, 0.000 CPU in 0.000 seconds (51% CPU, 285714 Lips)
Option = [106, 115, 111, 110].

Ложная точка выбора исходит из определения list//1 нетерминала. Разница в производительности заключается в том, что в то время как call//1 позволяет нам просто получить доступ к неявному аргументу списка, нетерминал list//1 выполняет копирование списка поэлементно этого неявного аргумента. Как следствие, производительность версии list//1 линейна для символов, следующих за префиксом --as=, в то время как производительность call//1 постоянна и не зависит от количества символов после префикса:

?- time(phrase(as_opt(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 54 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 2700000 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...] ;
% 4 inferences, 0.000 CPU in 0.000 seconds (69% CPU, 137931 Lips)
false.

?- time(phrase(as_opt2(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 6 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 333333 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...].

Ответ 3 · рейтинг 1 · CapelliC

Я думаю, что вы не должны использовать глобальные переменные. Вместо этого добавьте аргумент(ы) в предложения DCG, чтобы вернуть значения:

anything([C|Cs]) --> [C], anything(Cs).
anything([]) --> [].

в противном случае остаток доступен как последний аргумент фразы/3 после успешного выполнения правил, "потребляющих" совпадение заголовка.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы