В
главке Векторы
говорилось о том, что
содержимое XML-элементов
может представляться векторами, списками и их комбинациями. Это очень
удобно при сборке
XML-термов, но в значительной степени осложняет их обработку. Проблема
в том, что функции-обработчики
должны рассматривать разнообразные случаи представления информации, что
способно в
значительной степени усложнить написание программ и сделать их
малочитаемыми. Для решения
этой проблемы (и не только) во Флэнге используются перечисления.
Перечисления позволяют
абстрагироваться от конкретного формата многоэлементной структуры,
концентрируя внимание
только на порядке, в котором элементы в структуру входят. Во Флэнге
предусмотрены
разнообразные виды перечислений, которые позволяют писать компактные и
эффективные
программы.
Рассмотрим пример. Заведем список, вектор и
таблицу:
?- list := [1,2,3,4,5];
true
?- vector:=newVector([11,12,13,14,15]);
true
?- table := newHash([1/1, 2/4, 3/9, 4/16, 5/25]);
true
Для работы с перечислением нужны четыре функции:
- Функция, порождающая перечисление.
Этих функций
несколько -
в зависимости от
того, что перечисляем.
- Функция
eValue/1,
возвращающая текущий элемент последовательности.
- Функция
eNext/1,
передвигающая указатель последовательности на следующий элемент,
делая его текущим..
- Функция
exhausted/1,
проверяющая, не закончилась ли последовательность.
Простейший вид перечислений организуется функцией enum/1,
которая
перебирает элементы списка
и вектора:
?- enumeration:=enum(^list);
true
?- eValue(^enumeration);
1
?- eNext(^enumeration);
ENUM
?- eValue(^enumeration);
2
?- enumeration:=enum(^vector);
true
?- eValue(^enumeration);
11
?- eNext(^enumeration);
ENUM
?- eValue(^enumeration);
12
?- exhausted(^enumeration);
fail
Функция eNext/1
возвращает саму последовательность, но со
сдвинутым указателем. Определим
функцию, суммирующую элементы последовательнсти:
sum(Enum) :- exhausted(Enum), 0; sum(Enum) :- V=eValue(Enum), V+sum(eNext(Enum));
Получаем:
?- sum(enum(^vector));
65
?- sum(enum(^list));
15
Таким образом, одна
и та же функция суммирует элементы совершенно
различных
структур. Например, если воспользуемся функциями eKeys/1
и eValues/1,
создающими
перечисления для ключей и значений таблиц, соответственно, то получим:
?- sum(eKeys(^table));
15
?- sum(eValues(^table));
55
Если вернуться к проблеме обработки содержимого
XML-элементов, то
упомянутые выше перечисления
здесь не очень помогут, поскольку не могут идти в "глубину" структур:
?- sum(enum([1, [2, 3]])); *** RunError: 1st argument of '+' must be integer, float or string
Причина в том, что
вторым элементом перечисления является не число, а
список [2, 3].
Для решения этой проблемы существует очень важная для нас версия
перечисления eFlat/1:
?- sum(eFlat([1, [2, 3]]));
6
?- sum(eFlat([[[[1]], 2, [newVector([3,[4]]),5]]]));
15
Именно функция eFlat/1,
"разворачивая" структуры,
позволяет обрабатывать содержимое XML-элементов, не
заботясь о том, насколько сложна комбинация списков и векторов,
образующих это содержимое. Например, для
функции
pushkin145() :- <book> AП-145 <author> А.С. Пушкин </author> <title> Евгений Онегин </title> </book> ;
получаем:
?- enum := eFlat(getXConts(pushkin145()));
true
?- eValue(^enum);
" AП-145 "
?- eNext(^enum);
ENUM
?- eValue(^enum);
<author> А.С. Пушкин </author>
?- eNext(^enum);
ENUM
?- eValue(^enum);
" "
?- eNext(^enum);
ENUM
?- eValue(^enum);
<title> Евгений Онегин </title>
?- eNext(^enum);
ENUM
?- eValue(^enum);
" "
?- eNext(^enum);
ENUM
?- eValue(^enum);
null
?- exhausted(^enum);
true
Среди перечислений
есть и специальные перечисления, работающие с
XML-термами. Например,
перечисление eXElem/1
перебирает все элементы XML-документа.
Другие перечисления, в частности, очень полезное eXElem/2, позволяющее
перебирать
элементы, удовлетворяющих условию в формате XPath, приведены в главке "XML-термы:
продолжение". Рассмотрим
пример использования eXElem/1. Программа, приведенная
ниже, получая на
вход HTML-файл,
возвращает список заголовков этого файла (то есть, фрагментов текста,
обрамленных
тегами h1..h6).
|