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)


arrow_upward
3
arrow_downward

Вы просите один из простейших нетерминалов 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

arrow_upward
3
arrow_downward

Есть лучшее решение, чем то, которое описано в мате, которое быстрее и избегает ложных точек выбора, но требует поддержки встроенного нетерминала 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

arrow_upward
1
arrow_downward

Я думаю, что вы не должны использовать глобальные переменные. Вместо этого добавьте аргумент(ы) в предложения 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