Я выяснил код для проверки равных a и b в списке, но не могу понять базовую рекурсию

s(A,A).
s(A,D):- l(A,B),s(B,C),r(C,D).

l([a|A],A).

r([b|A],A).

Приведенный выше код в прологе проверяет, имеют ли заданные входные данные списка равные a и b или нет.

Такие как

s([a,a,b,b],[]).
True.

Это включает в себя рекурсию и списки различий. Может кто-нибудь объяснить, как лежащие в основе проверки рекурсии равны a и b шаг за шагом.

2 ответа

Решение

Различия в списке нелегко понять, если рассуждать о них на низком уровне.

Поэтому я сначала рекомендую более высокоуровневое представление:

За все, ваш предикат s/2 описывает список. Я говорю "описывает", потому что он не только "проверяет", но также генерирует и дополняет такие списки, если мы хотим!

Мы можем прочитать каждую цель s/2 как "а затем некоторые элементы списка".

Итак, на минуту забудем об аргументах и ​​рассмотрим абстрактную структуру предиката. я использую (-->)/2 сейчас вместо (:-)/2 чтобы прояснить, что я говорю о небольшой вариации предиката, где я просто игнорирую аргументы:

s -> [].
s -> l, s, r.

Мы можем сделать то же самое с l/2 а также r/2:

л -> [а].
r -> [b].

Это то, что предикаты описывают в абстрактном высокоуровневом представлении списков. В этой записи мне не нужно бороться со списком различий и аргументов. Вместо этого я могу сосредоточиться непосредственно на сути программы.

Очевидно, что вы можете легко перевести такой высокоуровневый код в код, который вы разместили. Фактически, Prolog выполняет этот перевод для вас, если вы обращаетесь к этому коду: он называется нотацией DCG. См. Dcg для получения дополнительной информации.

Итак, теперь понятно s//0 описывает список, который либо пуст, либо:

  • список описан l//0
  • а затем список, описанный s//0
  • а затем список, описанный r//0,

поскольку l//0 описывает список с одним элементом a, а также r//0 описывает список с одним элементом b ясно, что в списках s//0 описывает, всегда есть одинаковое количество a с и b s.

Мы используем phrase/2 вызвать DCG. Например:

? - фраза (с, Ls).
Ls = [];
Ls = [a, b];
Ls = [a, a, b, b];
Ls = [a, a, a, b, b, b].

Если вы начнете рассуждать о рекурсии явно, вы не добьетесь большого прогресса, потому что быстро становится слишком сложно отследить точные шаги, которые выполняет движок Prolog, и принять во внимание все возможности. Я рекомендую вам сосредоточиться на значении ваших предикатов и попытаться понять, что они на самом деле описывают.

РЕДАКТИРОВАТЬ: Если вы хотите явно рассуждать об аргументах, может помочь алгебраическая аналогия: мы можем рассматривать каждую пару аргументов как описание списка как " разницу " между двумя списками, разницу между списками, также по аналогии с дифференциалом, используемым в исчисление.

Например, "разница" между [X,Y,Z|Rs] а также Rs является [X,Y,Z], Следовательно, хотя бы символически, мы можем написать:

Обозначим через L, L 0, L 1 и L 2 списки, которые описываются такими различиями во втором пункте:

Алгебраически мы можем думать о L как о " сумме " (объединении) других списков:

Для других списков у нас есть:

Итак, в сумме имеем:

Обратите внимание, что для понимания этого не требуется рекурсия. Вместо этого важна скорее связь между аргументами. Лично я также нахожу такой вывод менее полезным, чем обратное: я думаю, что гораздо важнее заметить этот паттерн при написании такого кода, потому что это означает, что вы можете вместо этого использовать нотацию DCG и значительно сократить количество аргументов, которые передаются вокруг!

s(  A, A).               % s(0).
s(  A,       D) :-       % s(n):-
  l(A, B),               %   before,
     s(B, C),            %   s(n-1),
  r(      C, D).         %   after.

l(  [a | A], 
         A ).
r(  [b | B], 
         B ).

вместе определим

%% 1 
s(          [a , b | B1], B1):-
          l([a | A1],
                 A1 ),
              s( A1,      %s0%
                 A1 ),    %s0%
          r(    [b | B1],
                          B1 ).

а также

%% 2 
s(      [a , a , b , b | B2], B2):-
      l([a | A2],
             A2 ),
          l([a | A1],              %s1%
                 A1 ),             %s1%
              s( A1,      %s0%     %s1%
                 A1 ),    %s0%     %s1%
          r(    [b | B1],          %s1%
                     B1 ),         %s1%
      r(            [b | B2],
                              B2 ).

а также

%% 3 
s(  [a , a , a , b , b , b | B3], B3):-
  l([a | A3],
         A3 ),
      l([a | A2],                            %s2%
             A2 ),                           %s2%
          l([a | A1],              %s1%      %s2%
                 A1 ),             %s1%      %s2%
              s( A1,      %s0%     %s1%      %s2%
                 A1 ),    %s0%     %s1%      %s2%
          r(    [b | B1],          %s1%      %s2%
                     B1 ),         %s1%      %s2%
      r(            [b | B2],                %s2%
                         B2 ),               %s2%
  r(                    [b | B3],
                                  B3 ).

и так далее и тому подобное.

Другие вопросы по тегам