После приведения формулы вида ПНФ к виду ССФ вы получаете формулу, в безквантовой матрице которой дизъюнкт содержит оба контранрных атома:. Как тогда проводить его унификацию, если в случае замены x на f(x) весь дизъюнкт обратится в единицу? |
Строки
Пример. Теперь попробуем немного модифицировать описанный предикат так, чтобы он преобразовывал строку не в список символов, а в список атомов.
Отличаться предыдущее решение будет заменой предиката frontchar на предикат fronttoken. Да еще надо не забыть в разделе описания предикатов заменить домен второго параметра со списка символов на список строк.
Запишем эту идею:
str_a_list("",[]). /* пустой строке по-прежнему соответствует пустой список */ str_a_list(S,[H|T]):– fronttoken(S,H,S1), /* H — первый атом строки S, S1 — остаток строки */ str_a_list(S1,T). /* T — список, состоящий из атомов, входящих в строку S1*/
Кстати, этот же предикат можно задействовать для создания списка слов, входящих в строку. Если слова разделены только пробелами, то он подойдет безо всяких изменений. Если же в строку могут входить еще и знаки препинания, то каждый из них попадет в итоговый список отдельным элементом. Для того чтобы получить список, элементами которого являются только слова, можно просто удалить из него все знаки препинания, что не сложно. К сожалению, работать эта идея будет только на английских словах. Русский текст этот предикат разобьет на отдельные символы, а не на слова.
Пример. Разработаем предикат, который будет преобразовывать список символов в строку. Предикат будет иметь два аргумента. Первым аргументом будет список символов, вторым — строка, образованная из элементов списка.
Базис рекурсии: пустому списку соответствует пустая строка. Шаг: если исходный список не пуст, то нужно перевести в строку его хвост, после чего, используя стандартный предикат frontchar, приписывать к первому элементу списка строку, полученную из хвоста исходного списка.
Запишем эту идею:
list_str([],""). /* пустой строке соответствует пустой список */ list_str([H|T],S):– list_str(T,S1), /* S1 — строка, образованная элементами списка T */ frontchar(S,H,S1). /* S — строка, полученная дописыванием строки S1 к первому элементу списка H */
Пример. Создадим предикат, который по строке и символу подсчитает количество вхождений этого символа в данную строку. Предикат будет иметь три аргумента: первые два — входные ( строка и символ), третий — выходной (количество вхождений второго аргумента в первый).
Решение, как обычно, будет рекурсивным. Рекурсия по строке, в которой ведется подсчет количества вхождений данного символа. Если строка пустая, то не важно, вхождения какого символа мы считаем, все равно ответом будет ноль. Это базис. Шагов рекурсии будет два в зависимости от того, будет ли первым символом строки символ, вхождения которого мы считаем, или нет. В первом случае нужно подсчитать, сколько раз искомый символ встречается в остатке строки, и увеличить полученное число на единицу. Во втором случае (когда первый символ строки отличен от символа, который мы считаем) увеличивать полученное число не нужно. При расщеплении строки на первый символ и хвост нужно воспользоваться уже знакомым нам предикатом frontchar.
char_count("",_,0). /* Любой символ не встречается в пустой строке ни разу*/ char_count(S,C,N):– frontchar(S,C,S1),!, /* символ C оказался первым символом строки S, в S1 — оставшиеся символы строки S */ char_count(S1,C,N1), /* N1 — количество вхождений символа C в строку S1 */ N=N1+1. /* N — количество вхождений символа C в строку S получается из количества вхождений символа C в строку S1 добавлением единицы */ char_count(S,C,N):– frontchar(S,_,S1), /* первым символом строки S оказался символ, отличный от исходного символа C, в S1 — оставшиеся символы строки S */ char_count(S1,C,N). /* в этом случае количество вхождений символа C в строку S совпадает с количеством вхождений символа C в строку S1 */
Пример. Попробуем разработать предикат, который по символу и строке будет возвращать первую позицию вхождения символа в строку, если символ входит в строку, и ноль, если не входит. У предиката будет три параметра. Первые два — входные — символ и строка, третий — выходной — первая позиция вхождения первого параметра во второй параметр или ноль.
Не самая легкая задачка, но мы с ней справимся. Можно, конечно, записать в качестве базиса, что в пустой строке не встречаются никакие символы, но мы пойдем другим путем.
Вначале с помощью предиката frontchar разделим исходную строку на первый символ и остаток строки. Если первым символом строки окажется тот самый символ, позицию которого мы ищем, значит, больше ничего делать не нужно. Ответом будет единица. В этом случае нам даже неважно, какие символы оказались в хвосте строки, поскольку мы ищем первое вхождение данного символа в строку.
В противном случае, если первым символом исходной строки является какой-то символ, отличный от искомого, нам нужно искать позицию вхождения символа в остаток строки. Если искомый символ найдется в хвосте, позиция вхождения символа в исходную строку будет на единицу больше, чем позиция вхождения этого символа в остаток строки. Во всех остальных ситуациях наш символ не встречается в исходной строке и, следовательно, мы должны означить третий аргумент нулем.
Давайте попробуем записать эти рассуждения.
str_pos(C,S,1):– frontchar(S,C,_),!. /* Искомый символ C оказался первым символом данной строки S */ str_pos(C,S,N) :– frontchar(S,_,S1), /* S1 — состоит из всех символов строки S, кроме первого, который отличается от искомого символа C */ str_pos(C,S1,N1), /* N1 — это позиция, в которой символ C встречается первый раз в хвосте S1 или ноль*/ N1<>0,!, /* если позиция вхождения символа C в строку S1 не равна нулю, то есть если он встречается в строке S1, / N=N1+1. /* то, увеличив позицию его вхождения на единицу, мы получим позицию его вхождения в исходную строку */ str_pos(_,_,0). /* искомый символ не входит в данную строку */
Пример. Создадим предикат, который будет заменять в строке все вхождения одного символа на другой символ. У предиката будет четыре параметра. Первые три — входные (исходная строка ; символ, вхождения которого нужно заменять; символ, которым нужно заменять первый символ); четвертым — выходным — параметром должен быть результат замены в первом параметре всех вхождений второго параметра на третий параметр.
Решение, как обычно, будет рекурсивным. Если строка пустая, значит, в ней нет символов, и, следовательно, заменять нечего. Результатом будет тоже пустая строка. Если же строка непустая, то мы должны разделить ее с помощью предиката frontchar на первый символ и строку, состоящую из остальных символов исходной строки.
Возможны два варианта. Либо первый символ исходной строки совпадает с тем, который нужно заменять, либо не совпадает.
В первом случае заменим все вхождения первого символа вторым символом в хвосте исходной строки, после чего, опять-таки с помощью предиката frontchar, приклеим полученную строку ко второму символу. В итоге в результирующей строке все вхождения первого символа будут заменены вторым символом.
Во втором случае, когда первый символ исходной строки не равен заменяемому символу, заменим в хвосте данной строки все вхождения первого символа на второй, после чего присоединим полученную строку к первому символу первоначальной строки.
str_replace("",_,_,""):–!. /* из пустой строки можно получить только пустую строку */ str_replace(S,C,C1,SO):– frontchar(S,C,S1),!, /* заменяемый символ C оказался первым символом строки S, S1 — остаток строки S */ str_replace(S1,C,C1,S2), /* S2 — результат замены в строке S1 всех вхождений символа C на символ C1 */ frontchar(SO,C1,S2). /* SO — результат склейки символа C1 и строки S2 */ str_replace(S,C,C1,SO):– frontchar(S,C2,S1), /* разделяем исходную строку S на первый символ C2 и строку S2, образованную всеми символами строки S, кроме первого */ str_replace(S1,C,C1,S2), /* S2 — результат замены в строке S1 всех вхождений символа C на символ C1 */ frontchar(SO,C1,S2). /* SO — результат соединения символа C1 и строки S2 */
Если нам понадобится предикат, который будет заменять не все вхождения первого символа на второй, а только первое вхождение первого символа, то нужно просто из первого правила удалить вызов предиката str_replace(S1,C,C1,S2).