DCG, который соответствует остальной части ввода

Это предикат, который делает то, что должен, а именно, собирает все, что осталось на входе, когда является частью DCG:

rest([H|T], [H|T], []).
rest([], [], []).

но я изо всех сил пытаюсь определить это как DCG ... Или это вообще выполнимо?

Это, конечно, не то же самое (хотя и при одинаковом использовании делает то же самое):

rest([H|T]) --> [H], !, rest(T).
rest([]) --> [].

Причина, по которой я думаю, что мне это нужно, заключается в том, что rest//1 является частью набора правил DCG, которые мне нужны для анализа ввода. Я мог бы сделать phrase(foo(T), Input, Rest), но тогда мне пришлось бы звонить еще phrase(bar(T1), Rest).

Скажем, я знаю, что все, что у меня осталось на входе, — это строка цифр, которую я хочу получить как целое число:

phrase(stuff_n(Stuff, N), `some other stuff, 1324`).

stuff_n(Stuff, N) -->
    stuff(Stuff),
    rest(Rest),
    {   number_codes(N, Rest),
        integer(N)
    }.

person Community    schedule 10.09.2014    source источник


Ответы (3)


Отвечаю на свой глупый вопрос:

@CapelliC дал работающее решение (+1). Он делает что-то, чего я не понимаю :-(, но настоящая проблема заключалась в том, что я не понимал проблему, которую пытался решить. Настоящая проблема заключалась в следующем:

Проблема

У вас есть входной список кодов, который вам нужно проанализировать. В результате должен получиться термин. Вы знаете довольно близко к началу этого списка кодов, как выглядят остальные. Другими словами, он начинается с «ключевого слова», определяющего содержимое. В некоторых случаях, после некоторой точки ввода, остальное содержимое не нужно разбирать: вместо этого оно собирается в результирующем термине в виде списка кодов.

Решение

Одно из возможных решений — разбить синтаксический анализ на два вызова phrase/3 (поскольку нет причин не делать этого?):

  1. Прочитайте ключевое слово (первый вызов phrase/3) и сделайте его атомом;
  2. Посмотрите в таблице, как должно выглядеть остальное;
  3. Анализировать только то, что необходимо проанализировать (второй вызов phrase/3).

Код

Итак, используя подход из (O'Keefe 1990) и используя преимущества library(dcg/basics), доступные в SWI-Prolog, с файлом rest.pl:

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

codes_term(Codes, Term) :-
    phrase(dcg_basics:nonblanks(Word), Codes, Codes_rest),
    atom_codes(Keyword, Word),
    kw(Keyword, Content, Rest, Term),
    phrase(items(Content), Codes_rest, Rest).

kw(foo, [space, integer(N), space, integer(M)], [], foo(N, M)).
kw(bar, [], Text, bar(Text)).
kw(baz, [space, integer(N), space], Rest, baz(N, Rest)).

items([I|Is]) -->
    item(I),
    items(Is).
items([]) --> [].

item(space) --> " ".
item(integer(N)) --> dcg_basics:integer(N).

Важно, что здесь остальное вообще не должно обрабатываться правилом DCG.

Пример использования

Это решение хорошо, потому что оно детерминировано и его очень легко расширить: просто добавьте предложения в таблицу kw/4 и правила item//1. (Обратите внимание на использование флага --traditional при запуске SWI-Prolog для списков кодов, разделенных двойными кавычками)

$ swipl --traditional --quiet
?- [rest].
true.

?- codes_term("foo 22 7", T).
T = foo(22, 7).

?- codes_term("bar 22 7", T).
T = bar([32, 50, 50, 32, 55]).

?- codes_term("baz 22 7", T).
T = baz(22, [55]).
person Community    schedule 10.09.2014
comment
Одно примечание: нотация обратной кавычки специфична для SWI. Стандартная нотация использует двойные кавычки. - person false; 10.09.2014
comment
Это замечание относится к SWI7. С set_prolog_flag(double_quotes,codes). также SWI 7 ведет себя так, как ожидалось. - person false; 10.09.2014
comment
Вы близки к пределу в 10 правок, который превратит ваш ответ в ответ сообщества-вики. - person false; 10.09.2014
comment
Вы потеряете право собственности на ответ после примерно 10 правок. Таким образом, дальнейшие голоса не улучшат вашу репутацию. - person false; 10.09.2014
comment
@false Ага :) ну, увеличение моей репутации не было причиной, по которой я все равно ответил на свой вопрос. Меня просто смутила глупость вопроса. - person ; 10.09.2014

Альтернативой (которая не оставляет точку выбора) является использование встроенного нетерминала call//1 с лямбда-выражением. Использование синтаксиса лямбда-выражения Logtalk для иллюстрации:

rest(Rest) --> call({Rest}/[Rest,_]>>true).

Однако это решение немного неприятно, поскольку оно использует переменную с двойной ролью в лямбда-выражении (которое вызывает предупреждение компилятора Logtalk). Пример использования:

:- object(rest).

    :- public(test/2).
    test(Input, Rest) :-
        phrase(input(Rest), Input).

    input(Rest) --> [a,b,c], rest(Rest).

    rest(Rest) --> call({Rest}/[Rest,_]>>true).
%   rest([C|Cs]) --> [C|Cs].    % Carlo's solution

:- end_object.

Предполагая, что указанный выше объект сохранен в исходном файле dcg_rest.lgt:

$ swilgt
...

?- {dcg_rest}.
*     Variable A have dual role in lambda expression: {A}/[A,B]>>true
*       in file /Users/pmoura/Desktop/dcg_rest.lgt between lines 13-14
*       while compiling object rest
% [ /Users/pmoura/Desktop/dcg_rest.lgt loaded ]
% 1 compilation warning
true.

?- rest::test([a,b,c,d,e], Rest).
Rest = [d, e].

Вы должны быть в состоянии получить те же результаты, используя другие реализации лямбда-выражений, такие как библиотека lambda Ульриха.

person Paulo Moura    schedule 10.09.2014
comment
Правильно ли я понимаю, что вы предлагаете это решение, потому что оно не оставляет точки выбора? Я почти религиозно стараюсь избегать предупреждений ... Я до сих пор не знаю, не лучше ли самое первое определение rest/3, не относящееся к DCG, в моем вопросе (которое все еще может использоваться phrase), чем любое взломанное правило DCG. - person ; 10.09.2014
comment
Предупреждение существует, потому что обычно использование переменной как переменной без лямбда и как лямбда-параметра является опечаткой или ошибкой программиста. Однако не в этом случае. Но я действительно думаю, что определение rest/3, отличное от DCG, не является элегантным решением, поскольку оно нарушает абстракцию, предоставляемую DCG, а также предполагает конкретный перевод правил грамматики в предикатные предложения. - person Paulo Moura; 10.09.2014
comment
... а также предполагает конкретный перевод грамматических правил в предикатные предложения: именно поэтому я почувствовал необходимость спросить. Спасибо, что нашли время объяснить! - person ; 11.09.2014

может быть

rest([C|Cs]) --> [C|Cs] .

по крайней мере, в SWI-Prolog он работает (я использовал библиотеку (dcg/basics), чтобы получить номер)

line(I,R) --> integer(I), rest(R).
?- phrase(line(N,R), `6546 okok`).
N = 6546,
R = [32, 111, 107, 111, 107] 
person CapelliC    schedule 10.09.2014
comment
Он оставляет точку выбора позади, не знаю почему. Вы знаете, что делает '$append'/3? (посмотрите расширенную версию). У меня на самом деле было это по пути, но я не мог понять, как это работает... - person ; 10.09.2014
comment
$append/3 идентичен append/3 — то же самое простое старое определение Пролога. Он определен в init.pl, а append/3 — в lists.pl. Я думаю, что может потребоваться избежать зависимостей от библиотеки списков при расширении DCG. - person CapelliC; 10.09.2014
comment
О точке выбора: cut должен подойти, а также может иметь смысл иметь его, иначе rest//1 легко встраивается - person CapelliC; 10.09.2014
comment
Что вы подразумеваете под легко встраиваемым? И где бы вы поставили разрез? Сразу после rest//1 в определении line//2 или после line//2, где бы оно ни использовалось? И да, было бы неплохо сделать его детерминированным, поскольку это предполагаемая семантика rest//2? - person ; 10.09.2014
comment
rest([C|Cs]) --› [C|Cs], !. Встроенная я имею в виду, что вместо ...,rest(R), ... мы можем написать ...,[C|Cs],{R=[C|Cs]}.... (не проверено, насколько я понял...) - person CapelliC; 10.09.2014
comment
Отрезок в конце rest//1 каким-то образом его ломает. Это также меняет то, на что оно расширяется. Встроенная версия, которую вы предлагаете, по своему поведению идентична использованию ее в качестве отдельного правила DCG (включая эффект отсечения). Вырезать, вероятно, нужно после вызова phrase/2? - person ; 10.09.2014
comment
вы правы, rest//1, вероятно, играет в серой зоне сложностей term_expansion. Конечно, эта проблема может быть истинным источником вдохновения для кода О'Кифа, который вы процитировали в своем собственном ответе (я не вижу вашего решения настолько ясным, как хотелось бы). Что касается $append/3, кажется, что он полностью убран из term_expansion, что я имею в виду, trace не показывает код, который мы видим с listing - person CapelliC; 10.09.2014
comment
Решение - мой ответ не ясен, и мне потребовалось несколько попыток, чтобы довести его до точки, в которой он подходит для моей проблемы. Но мое решение - вообще не разбирать остальные! :-) Я предполагаю, что основная проблема заключается в несоответствии импеданса между синтаксическим анализом списка (атомов) и попыткой преобразовать ввод извне в термины Пролога (особенно когда ввод из потока и phrase_from_file не работает). - person ; 10.09.2014
comment
В этом ответе используются две функции, несовместимые с системами ISO, такими как SICStus: запись [X | Xs] в теле правила является ошибкой с использованием обратных кавычек вместо двойных кавычек (set_prolog_flag(double_quotes, codes) исправляет это) - person false; 10.09.2014