Списочные домены
Объявление списочных доменов осуществляется очень просто – надо справа от домена элемента списка указать звёздочку. Например:
domains
list = integer*. % Список целых чисел L=[8,-36,-209,0]
ul = unsigned*. % Список беззнаковых L=[8,36,209,0]
числа = real*. % Список вещественных L=[8.2,-36.189]
строки = string*. % Список строк L=[“QW”,“Пролог”,”5t”]
символы = char*. % Список символов L=[‘t’,’Ф’,’\61’]
Последний символ в списке символов указан посредством его ASCII-кода. Также легко создавать списки списков и т.д. Например:
domains
list = integer**. % Список списков целых чисел
ul = unsigned**. % Список списков беззнаковых целых
числа = real**. % Список списков вещественных чисел
строки = string**. % Список списков строк
символы = char**. % Список списков символов
Visual Prolog содержит ряд списочных доменов, объявленных в классе core:
integer_list = integer*.
integer_list_list = integer_list*.
integer_list_list_list = integer_list_list*.
unsigned_list = unsigned*.
unsigned_list_list = unsigned_list*.
unsigned_list_list_list = unsigned_list_list*.
real_list = real*.
real_list_list = real_list*.
real_list_list_list = real_list_list*.
char_list = char*.
char_list_list = char_list*.
char_list_list_list = char_list_list*.
string_list = string*.
string_list_list = string_list*.
string_list_list_list = string_list_list*.
symbol_list = symbol*.
symbol_list_list = symbol_list*.
symbol_list_list_list = symbol_list_list*.
Полный перечень списочных доменов содержится в описании класса core.
Ввод-вывод списков
Ввод списка с клавиатуры или из файла осуществляется предикатом X=read(). Этот предикат ожидает ввода с клавиатуры терма X. Тип терма можно указать принудительно, с помощью предиката hasDomain(Domain,X). Если тип терма не указан, то Visual Prolog автоматически во время компиляции определяет тип терма на основе последующих операций над ним, производимых в исходном тексте. Если Пролог во время компиляции не может определить тип терма, то ему следует “помочь”, явно указав перед вызовом функции read() тип вводимых данных с помощью предиката hasDomain(Domain,X).
Для ввода списка целых чисел посредством предиката X=read() необходимо набрать с клавиатуры последовательность, например [7,8,1,2,0,-3,7] или любой другой, в которой элементы списка перечислены через запятую и взяты в квадратные скобки. После ввода списка с клавиатуры, он будет унифицирован с переменной X=[7,8,1,2,0,-3,7]. Для вывода списков можно использовать предикат write(X).
В этой лабораторной работе мы создадим не GUI-проект, а консольный проект. Это упростит выполнение примеров и задач. Консольное приложение стартует с предиката run().
Например, вот такая цель:
run():-init(),
hasDomain(string_list,X),
X=read(),clearInput(),
write(X),nl,
_=readchar().
позволяет ввести список строк с клавиатуры и для контроля вывести его на экран. Предикат clearInput() вызывать сразу после ввода с клавиатуры не обязательно, но желательно, так как он освобождает буфер клавиатуры от введённых данных. Если этого не делать, то данная программа выполнится, и окно консоли сразу закроется, так как предикат _=readchar(), стоящий в конце цели, выполнится без ожидания ввода с клавиатуры, ибо предикат ввода X=read() оставляет данные в буфере.
Пример 0. Определение длины списка целых чисел, вводимых с клавиатуры:
implement main
open core, console
constants
className = "com/visual-prolog/main".
classVersion = "$JustDate: $$Revision: $".
class predicates
len : (Elem* Список, unsigned Длина) procedure (i,o).
length : (Elem* Список, unsigned Накопитель, unsigned Длина) procedure (i,i,o).
clauses
classInfo(className, classVersion).
len([_|L],D) :- !, len(L,D1), D=D1+1.
len([],0).
length([_|L],T,D) :- T1=T+1, !, length(L,T1,D).
length([],D,D).
run():-init(),
hasDomain(integer_list,X),
X=read(),clearInput(),
write("Список: ",X),nl,
len(X,D),
write("Длина списка = ",D),nl,
length(X,0,D1),
write("Длина списка = ",D1),nl,
_=readchar().
end implement main
goal
mainExe::run(main::run).
В этом примере длина списка определяется двумя способами. Первый способ использует нехвостовую рекурсию:
len([_|L],D) :- !, len(L,D1), D=D1+1.
len([],0).
Второй способ использует хвостовую рекурсию с дополнительным аргументом-счётчиком Накопитель, в котором накапливается текущая сумма до тех пор, пока список не станет пуст:
length([_|L],T,D) :- T1=T+1, !, length(L,T1,D).
length([],D,D).
Задача 0. Перепишите два предиката из примера 0 в виде функций.