Пример. Теперь попробуем немного модифицировать описанный предикат так, чтобы он преобразовывал строку не в список символов, а в список атомов.
Отличаться предыдущее решение будет заменой предиката 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).