Использование Prolog DCG для разделения строки - PullRequest
2 голосов
/ 23 мая 2011

Я пытаюсь использовать DCG, чтобы разбить строку на две части, разделенные пробелами. Например. 'abc def' должно вернуть мне "abc" & "def". Программа и DCG приведены ниже.

main:-
    prompt(_, ''),
    repeat,
    read_line_to_codes(current_input, Codes),
    (
        Codes = end_of_file
    ->
        true
    ;
        processData(Codes),
        fail
    ).

processData(Codes):-
    (
        phrase(data(Part1, Part2), Codes)
    ->
        format('~s, ~s\n', [ Part1, Part2 ])
    ;
        format('Didn''t recognize data.\n')
    ).

data([ P1 | Part1 ], [ P2 | Part2 ]) --> [ P1 | Part1 ], spaces(_), [ P2 | Part2 ].
spaces([ S | S1 ]) --> [ S ], { code_type(S, space) }, (spaces(S1); "").

Это работает правильно. Но я обнаружил, что необходимость набирать [ P1 | Part1 ] & [ P2 | Part2 ] была действительно многословной. Итак, я попытался заменить все экземпляры [ P1 | Part1 ] w / Part1 и аналогично w / [ P2 | Part2 ] в определении data, то есть следующее.

data(Part1, Part2) --> Part1, spaces(_), Part2.

Гораздо проще набрать, но это дало мне ошибку Arguments are not sufficiently instantiated. Таким образом, похоже, что несвязанная переменная автоматически не интерпретируется как список кодов в DCG. Есть ли другой способ сделать это менее многословным? Я намереваюсь использовать DCG, где бы я использовал регулярные выражения на других языках программирования.

1 Ответ

4 голосов
/ 24 мая 2011

Ваша интуиция верна; процедура расширения термина для DCG (по крайней мере, в SWI-Prolog, но должна применяться к другим) с вашей измененной версией data дает следующее:

?- listing(data). 

data(A, D, B, F) :-
    phrase(A, B, C),
    spaces(_, C, E),
    phrase(D, E, F).

Как видите, переменные Part1 и Part2 части вашего правила DCG были снова интерпретированы как вызовы phrase/3, а не списки; вам нужно явно указать, что они являются списками для них, которые будут рассматриваться как таковые.

Я могу предложить альтернативную версию, которая является более общей. Рассмотрим следующую группу правил DCG:

data([A|As]) --> 
    spaces(_), 
    chars([X|Xs]), 
    {atom_codes(A, [X|Xs])}, 
    spaces(_), 
    data(As).
data([]) --> [].

chars([X|Xs]) --> char(X), !, chars(Xs).
chars([]) --> [].

spaces([X|Xs]) --> space(X), !, spaces(Xs).
spaces([]) --> [].

space(X) --> [X], {code_type(X, space)}. 
char(X) --> [X], {\+ code_type(X, space)}.

Взгляните на первый пункт сверху; Правило data теперь пытается сопоставить пробелы от 0 до многих (как можно больше из-за разреза), а затем от одного до многих непробельных символов для построения атома (A) из кодов, затем снова пробелы 0-ко-многим, затем рекурсивно, чтобы найти больше атомов в строке (As). В итоге вы получите список атомов, которые появились во входной строке без пробелов. Вы можете включить эту версию в свой код следующим образом:

processData(Codes) :-
    % convert the list of codes to a list of code lists of words
    (phrase(data(AtomList), Codes) ->
        % concatenate the atoms into a single one delimited by commas
        concat_atom(AtomList, ', ', Atoms),
        write_ln(Atoms)
    ;
        format('Didn''t recognize data.\n')
    ).

Эта версия разбивает строку на любое количество пробелов между словами, даже если они появляются в начале и конце строки.

...