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

Этот ответ: Очень простой синтаксис пролога 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) сам, но тем временем любая помощь будет очень кстати.

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


person Emacs The Viking    schedule 06.09.2013    source источник


Ответы (3)


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

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

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

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

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

as_opt(Option) --> "--as=", list(Option).
person mat    schedule 06.09.2013
comment
Мат, хороший ответ. Я думаю, будучи нубом с правилами Prolog и DCG, мне еще предстоит оценить нюансы! Я буду работать с этим, и, без сомнения, его отслеживание поможет мне лучше понять его. Спасибо. Хороший лаконичный ответ. - person Emacs The Viking; 06.09.2013
comment
облом. Надо было попробовать код, слишком взволнован! Это пока не работает для меня. Он выводит [] в качестве значения опции. Буду стараться... скоро вернусь, наверное... - person Emacs The Viking; 06.09.2013
comment
мат, мне кажется порядок правил неверный. Если я поставлю пустой список в последний раз, все будет выглядеть намного лучше! Как вы думаете, это упущение с вашей стороны? - person Emacs The Viking; 06.09.2013
comment
В ПОРЯДКЕ. Теперь я принял этот ответ, потому что он работает для меня, как только я поменял правила. Я могу работать с этим, а также немного лучше понимаю Пролог. Спасибо мат. - person Emacs The Viking; 06.09.2013
comment
Если строка Ls, которую вы хотите проанализировать, состоит из --as=‹nonempty›, то ?- phrase(as_opt(O), Ls) не может быть успешно завершена с O=[]. Всегда используйте интерфейс phrase/2 для DCG. Есть случаи, когда перестановка правил имеет смысл (чтобы получить самое длинное совпадение ввода), но, на мой взгляд, это не один из них, потому что у вас может быть несколько вариантов, и я, за исключением вашего основного предиката интерфейса, будет выглядеть очень похоже на options([O|Os]) --> as_opt(O), " ", options(Os)., где разделительный пробел определенно не должен быть частью анализируемой опции (хотя вы изначально просили об этом). - person mat; 06.09.2013
comment
Эй, мат, я использую предикат gprolog argument_list, чтобы получить параметры, они отображаются в виде списка атомов. Затем я просто перебираю список, используя правило запуска DCG, чтобы установить свои данные. Хорошо работает сейчас. Спасибо всем, кто ответил. - person Emacs The Viking; 08.09.2013

Есть лучшее решение, чем то, которое описано в мате, которое быстрее и избегает ложных точек выбора, но требует поддержки встроенного нетерминала 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|...].
person Paulo Moura    schedule 07.09.2013
comment
Ууууух! Пауло, это кажется очень хорошим ответом, но с точки зрения понимания он просто выше моего понимания! Я использую Prolog всего месяц или три. Я понимаю, что вы имеете в виду, говоря о точках выбора, это действительно хороший момент, и мне все еще нужно полностью обдумать его. Реальность такова, что мой DCG будет выполняться один раз, чтобы проанализировать, вероятно, не более четырех или пяти аргументов командной строки; это просто должно работать. Спасибо хоть. :) - person Emacs The Viking; 07.09.2013
comment
Нетерминальный вызов call//1 преобразуется во встроенный вызов предиката call/3, в котором первый аргумент (замыкание rest(Rest)) является общим, а два дополнительных аргумента (в вызове call/3) являются двумя неявными аргументами правила DCG. Определение предиката rest/3 просто объединяет аргумент закрытия с первым (он же входным) неявным аргументом. Существует также альтернативное решение, использующее встроенный нетерминал call//1 с лямбда-выражением в качестве аргумента, который не требует вспомогательного предиката (например, rest/3), но это неприятный трюк, и я не буду его публиковать. - person Paulo Moura; 08.09.2013
comment
Пауло, нам никогда не следует мненеть в реальной жизни. Если бы твой мозг и мой антимозг объединились, взрыв мог быть смертельным... спасибо! :) - person Emacs The Viking; 08.09.2013

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

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

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

person CapelliC    schedule 06.09.2013
comment
Я относительно новичок в Прологе. Я должен прочитать руководство для фразы / 3, я думаю, поскольку я вызываю правило запуска вручную в итеративном цикле по аргументам командной строки. Я использовал глобальные переменные, потому что пишу большое приложение с помощью gprolog. Разбор параметров командной строки — это лишь одна из вещей, которые мне нужно было сделать. Это упрощает другие части кода, и мои глобальные переменные строго читаются только после того, как они установлены в модуле командной строки. Я полностью понимаю ваши оговорки, хотя. - person Emacs The Viking; 06.09.2013