Разделить список на две части в prolog

#list #prolog

#Список #prolog

Вопрос:

Я пытаюсь разделить список на 2 в prolog. Но я все еще новичок в этом, и любая помощь была бы высоко оценена.

Моя проблема в том, что:

Реализуйте предложение choose(N,L, R,S), которое выбирает N элементов из L и помещает их в R, а остальные элементы из L оставляют в S

Это то, что я пробовал до сих пор:

 split(0,_L1,_L2,_L4). 
split(X,[H|T],L1,T):-
     X>0,
     X1 is X-1,
     split(X1,T,[H|L1],T).
  

Когда я пытаюсь убежать

 split(2,[1,2,4,5],X,Y).
false
  

Вот какой результат я получаю. Что я делаю не так?

Ответ №1:

Если X > 0, то первый элемент L списка также должен быть первым элементом R списка. Например, это должно содержать: split(1, [a | Rest], [a], Rest) . Если мы хотим, чтобы эта взаимосвязь сохранялась, мы должны выразить ее в заголовке правила.

Поэтому ваше второе предложение должно выглядеть примерно так:

 split(X, [H|T], [H|L1], Rest) :-
     X > 0,
     X1 is X - 1,
     split(X1, T, L1, Rest).
  

Это правильно удаляет префикс, но остальное еще не правильно:

 ?- split(2, [1, 2, 4, 5], R, S).
R = [1, 2|S] ;
false.
  

Вам нужно еще раз подумать о случае, когда 0 элементов должны быть разделены. Каким должен быть результат split(0, [a, b, c], R, S) ?

Ответ №2:

Ваша самая большая проблема заключается в том, как вы конструируете / деконструируете 3-й аргумент.

И вы упускаете особый случай.

Большинство рекурсивных задач имеет пару специальных, обычно завершающих, случаев и один общий случай. У этой проблемы есть два особых случая.

Общий случай прост. Вы заметите здесь, что мы объединяем третий аргумент с [X|Pfx] . Это добавляет X к началу результата left / prefix и дает нам его (вероятно, несвязанный) конец. Этот хвост передается при рекурсии.

 partition( N , [X|Xs] , [X|Pfx] , Sfx ) :-
    N > 0 ,
    N1 is N-1 ,
    partition( N1, Xs, Pfx, Sfx )
    .
  

[Вы могли заметить, что по ходу работы мы создаем список в 3-м аргументе (список префиксов)… но это не легальный список: хвост предположительно несвязан. Мы позаботимся об этом в конце.

Итак … это касается общего случая. Но как мы узнаем, когда закончим?

Один особый / завершающий случай — это когда исходный список исчерпан. Если это произойдет до N уменьшения на 0 , мы закончили. Мы можем обработать это следующим образом:

 partition( _ , [], [], [] ).
  

На самом деле нас не волнует, какое значение N имеет, но может быть полезно включить ограничение, которое N >= 0 . Что здесь происходит, так это то, что когда исходный список (2-й аргумент) исчерпан и является пустым списком, мы (1) закрываем список префиксов (3-й аргумент) и объединяем список суффиксов с пустым списком.

Следующий особый случай — это когда N окончательно уменьшается до нуля. Это не сложнее:

 partition( 0 , Sfx, [], Sfx ).
  

Мы объединяем все, что осталось от исходного списка, с помощью списка суффиксов (4-й аргумент) и закрываем список префиксов пустым списком.

Оставшийся частный случай — это когда N окончательно уменьшается до 0 . Это так же просто. Вот:

 partition( 0 , Xs, [], Xs ).
  

Соедините все это вместе, и вы получите:

 partition( _ , []     , []      , []  ).
partition( 0 , Sfx    , []      , Sfx ).
partition( N , [X|Xs] , [X|Pfx] , Sfx ) :-
  N > 0 ,
  N1 is N-1,
  partition( N1, Xs , Pfx , Sfx ).