Функции
В XQuery предусмотрена библиотека предопределенных функций [11], а также предоставляется возможность определения пользователями их собственных функций. При вызове аргументы связываются с параметрами функции, и выполняется ее тело, порождая результат вызова функции. Если тип параметра функции не указан, этот параметр может принимать значения любого типа. Если не указан тип результата функции, то функция может возвращать значение любого типа.
В следующем примере определена функция с именем highbid, в качестве параметра использующая узел-элемент и возвращающая десятичное значение. Функция интерпретирует свой параметр как элемент item и извлекает номер товара. Затем она находит и возвращает самую крупную ставку (bid-amount), которая была зафиксирована для товара с этим номером. define function highbid(element $item) returns decimal { max(document("bids.xml") //bid [itemno = $item/itemno]/bid-amount) } highbid(document("items.xml") //item [itemno = "1234"])
Типы, используемые при объявлении типов аргументов и результата в определении функции, могут быть простыми, как decimal, или более сложными типами, например, элементами или атрибутами.
В XQuery не поддерживается перегрузка функций, определенных пользователем, т. е. не допускается наличие двух функций с одинаковыми именами. Тем не менее, некоторые встроенные функции являются перегруженными. Например, функция string может преобразовывать в строку аргумент почти любого типа.
Аргументы при вызове функции должны соответствовать объявленным типам параметров функции. С этой целью аргумент функции числового типа может быть приведен к объявленному типу параметра с помощью иерархии приведения integer -> decimal -> float -> double. Аргумент также считается удовлетворяющим условию вызова, если тип этого аргумента является производным типом (т.е. подтипом) объявленного типа параметра. Если функция, ожидающая атомарное значение в качестве параметра, вызывается с аргументом, являющимся элементом, то до передачи аргумента функции из него извлекается типизированное значение элемента и проверяется на совместимость с ожидаемым типом параметра.
Значение, производимое телом функции, должно также соответствовать возвращаемому типу, объявленному в определении; используются обычные правила проверки соответствия типов параметров.
Следующий пример иллюстрирует, как пользователь может написать функцию, которая предоставляет значение по умолчанию для отсутствующих данных. Функция с именем defaulted принимает два параметра: узел-элемент (возможно, отсутствующий) и значение по умолчанию. Если элемент присутствует и имеет непустое значение, функция возвращает это значение. Если же элемент отсутствует или пуст, функция возвращает значение по умолчанию. define function defaulted (element? $e, anySimpleType $d) returns anySimpleType { if (empty($e))then $d else if (empty($e/_)then $d else data($e) }
С помощь этой функции запрос Q5 можно переписать (здесь отсутствующие или пустые элементы commission и bonus считаются равными нулю). for $e in $emps return <emp> { $e/name, <pay> | {$e/salary + defaulted ($e/commission,0) + defaulted ($e/bonus,0)} </pay> } </emp> sortby(pay)
Функция, в теле которой присутствует вызов самой себя, называется рекурсивной (recursive), и две функции, в теле каждой из которых присутствует вызов другой функции пары, называются взаимно рекурсивными (mutually recursive). В следующем примере рекурсивная функция depth может быть вызвана для элемента и возвращает глубину элемента в иерархии, начинающейся с аргумента вызова. Если у элемент-аргумента отсутствуют потомки, глубина иерархии равна единице. Иначе глубина иерархии на единицу больше максимального значения глубины всех иерархий, корнем которых является потомок элемента-аргумента. Это значение вычисляется путем рекурсивного вызова функции depth. define function depth(element $e) returns integer { if (empty($e/*)) then 1 else 1 + max (for $c in $e/* return depth($c)) } depth(document("bids.xml"))