
- •7. Списки в Прологе
- •Определение и унификация
- •Упражнения на унификацию
- •Рассмотрим ещё несколько примеров
- •7.2 Операции на списках
- •7.2.1 Поиск элемента в списке
- •7.2.2 Создание списков
- •7.2.2.1 Простейший случай
- •7.2.2.2 Получение списка из двух существующих списков
- •7.2.2.3 Получение списка по условию
- •7.2.2.4 Вычеркивание элементов списка
- •7.2.2.5 Замена элементов списка
- •7.2.2.6 Проверка, является ли аргумент списком.
- •7.2.2.7 Подсчет количества элементов в списке.
- •7.2.2.9 Вывод списка на дисплей.
- •7.3 Задачи на списках
- •7.3.1 Нахождение путей в связанном неориентированном графе
- •7.3.2 Синтаксический анализатор на дка
7.2.2.3 Получение списка по условию
Зададимся целью, получить новый список из исходного, включая в него лишь те элементы, которые будут удовлетворять некому условию.
Например, из списка [1,12,3,14,5,8], выберем лишь элементы большие чем 6.
Нам надо реализовать три предложения.
1. если элемент удовлетворяет условию, то мы добавляем его к результирующему списку.
2. если нет - мы должны отказаться от элемента.
3. если список пуст, мы возвратим пустой список.
Реализация первого предложения (Р1) будет таким:
решето([X| Tail],[X|Result]):-
X > 6, % is X greater than 6 ?
решето(Tail,Result). % if so then go find the rest
Второе правило (Р2) - отказ от ненужного элемента списка запишется так:
решето([ThrowAway|Tail],Result):- % выбрасываем голову и
решето(Tail,Result). % рассматриваем хвост
Последнее предложение должно выглядеть, например, так:
решето([],[]).
Проверка
?- решето([1,12,3,14,5,8], Result).
Result = [12, 14, 8]
Теперь посмотрим, как работает программа на таком запросе
? - решето([1,12,3,14,5,8], Result).
Получается, что все начинается со второго предложения.
Здесь подцель 1>6 явно терпит неудачу, так что мы переходим к третьему предложению и пробуем выражения решето([1,12,3,14,5,8], Result) в таком виде: решето([1|12,3,14,5,8],Result), что означает вызов уже в виде:решето([12,3,14,5,8],Result), а это соответствует второму предложению.
Теперь подцель 12>6 успешна, и мы делаем запрос цели решето([3,14,5,8],Result). Но, обратите внимание, что в этом предложении происходит засылка «успешного» числа 12 в результирующий список.
Шагая далее через наш пример, мы видим, пока наконец мы не добираемся до цели, во втором предложении успешными будут также 14 и 8. А по исчерпанию списка выполнится первое предложение, что даст унификацию Result с [].
Теперь, выходя из рекурсии, мы будем благодаря второму предложению получать список в обратном порядке: сначала [8], затем [14,8], и наконец [12,14,8], прежде, чем получим успешный ответ.
Но правильный ответ будет таким:
Result = [12, 14, 8] ;
Result = [12, 14] ;
Result = [12, 8] ;
Result = [12] ;
Result = [14, 8] ;
Result = [14] ;
Result = [8] ;
Result = [].
(Здесь, как всегда для нового запроса – нажимаем Enter)
Если нам нужен один, но самый полный ответ, то первое предложение (Р1) нужно
записать так:
решето([X| Tail],[X|Result]):-
X > 6, !, % Поставлен “восклицательный знак”
решето(Tail,Result). % if so then go find the rest
Теперь результат будет кратким и полным:
?- решето([1,12,3,14,5,8], Result).
Result = [12, 14, 8].
?-
Никаких других решений не выдаётся.
То, что было использовано в написании, является предикатом cut (англ. отсечение), но пишется как восклицательный знак. Его назначение – ограничить пространство поиска решений, и тем самым повысить эффективность процедур, реализующих программы на прологе.
Его применение изменяет декларативную логику программы и поэтому, может привести к неправильным решениям. Если это случается, то такое отсечение называется «красным», в отличие от «зелёного» отсечения (как в нашем случае). У нас получается только одно, НО правильное решение.
Вывод. Применение отсечений требуем тщательной проверки