This [[Module Library|XQuery Module]] adds some useful higher-order functions, additional to the [[Higher-Order Functions]] provided by the official specification.
With {{Announce|Version 11}}, many functions have been removed in favor of new features of XQuery 4:
{|
|- valign="top"
| '''BaseX 10'''
| '''XQuery 4'''
|- valign="top"
| {{Code|hof:drop-while}}
| [https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-drop-while <code>fn:items-starting-where</code>]
|- valign="top"
| {{Code|hof:id}}
| [https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-identity <code>fn:identity</code>]
|- valign="top"
| {{Code|hof:until}}
| [https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-iterate-while <code>fn:iterate-while</code>]
|- valign="top"
| {{Code|hof:take-while}}
| [https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-take-while <code>fn:items-before</code>]
|}
=Conventions=
{| width='100%'
|-valign="top"| width='120' | '''SignaturesSignature'''|{{Func|<pre>hof:fold-left1|( $seq input as item()+, $f action as function(item()*, item()) as item()*|) as item()*}}</pre>|-valign="top"
| '''Summary'''
|Works the same as [[Higher-Order Functions#fn:fold-left|fn:fold-left]], but does not need a seed, because the sequence must be non-empty.
|-valign="top"
| '''Examples'''
|
* {{Code|hof:fold-left1(1 to 10, function($a, $b) { $a + $b })}} returns {{Code|55}}.
* {{Code|hof:fold-left1((), function($a, $b) { $a + $b })}} throws {{Code|XPTY0004}}, because {{Code|$seq}} has to be non-empty.
|}
==hof:until==
{| width='100%'
|-
| width='120' | '''Signatures'''
|{{Func|hof:until|$pred as function(item()*) as xs:boolean, $f as function(item()*) as item()*, $start as item()*|item()*}}
|-
| '''Summary'''
|Applies the predicate function {{Code|$pred}} to {{Code|$start}}. If the result is {{Code|false}}, {{Code|$f}} is invoked with the start value – or, subsequently, with the result of this function – until the predicate function returns {{Code|true()}}.
|-
| '''Examples'''
|
* Doubles a numeric value until a maximum is reached:
<syntaxhighlight lang="xquery">
hof:until(
function($output) { $output ge 1000 },
function($input ) { 2 * $input },
1
)
</syntaxhighlight>
* Calculates the square-root of a number by iteratively improving an initial guess:
<syntaxhighlight lang="xquery">
let $sqrt := function($input as xs:double) as xs:double {
hof:until(
function($result) { abs($result * $result - $input) < 0.00001 },
function($guess) { ($guess + $input div $guess) div 2 },
$input
)
}
return $sqrt(25)
</syntaxhighlight>
* Returns {{Code|OK}}, as the predicate is evaluated first:
<syntaxhighlight lang="xquery">
hof:until(
function($_) { true() },
function($_) { error() },
'OK'
)
</syntaxhighlight>
|}
{| width='100%'
|-valign="top"| width='120' | '''SignaturesSignature'''|{{Func|<pre>hof:scan-left|( $seq input as item()*, $start zero as item()*, $f action as function(item()*, item()) as item()*|) as item()*}}</pre>|-valign="top"
| '''Summary'''
|This function is similar to [[Higher-Order Functions#fn:fold-left|fn:fold-left]], but it returns a list of successive reduced values from the left. It is equivalent to:
<syntaxhighlight pre lang="'xquery"'>declare function hof:scan-left($seqinput, $acc, $faction) { if(empty($seqinput)) then $acc else (
$acc,
hof:scan-left(tail($seqinput), $faction($acc, head($seqinput)), $faction)
)
};
</syntaxhighlightpre>|-valign="top"
| '''Examples'''
|
* Returns triangular numbers:
<syntaxhighlight pre lang="'xquery"'>
hof:scan-left(1 to 10, 0, function($a, $b) { $a + $b })
</syntaxhighlight>|} ==hof:take-while== {| width='100%'|-| width='120' | '''Signatures'''|{{Func|hof:take-while|$seq as item()*, $pred as function(item()) as xs:boolean|item()*}}|-| '''Summary'''|The function returns items of <code>$seq</code> as long as the predicate <code>$pred</code> is satisfied. It is equivalent to:<syntaxhighlight lang="xquery">declare function hof:take-while($seq, $pred) { if(empty($seq) or not($pred(head($seq)))) then () else ( head($seq), hof:take-while(tail($seq), $pred) )};</syntaxhighlight>|-| '''Examples'''|* Computes at most 100 random integers, but stops if an integer is smaller than 10:<syntaxhighlight lang="xquery">hof:take-while( (1 to 100) ! random:integer(50), function($x) { $x >= 10 })</syntaxhighlightpre>
|}
{| width='100%'
|-valign="top"| width='120' | '''SignaturesSignature'''|{{Func|<pre>hof:top-k-by|( $seq input as item()*, $sort-key as function(item()) as item(), $k as xs:integer|) as item()*}}</pre>|-valign="top"
| '''Summary'''
|Returns the {{Code|$k}} items in {{Code|$seqinput}} that are greatest when sorted by the result of {{Code|$fkey}} applied to the item. The function is a much more efficient implementation of the following scheme:<syntaxhighlight pre lang="'xquery"'>(for $x item in $seqinput order by $sort-key($xitem) descending return $xitem
)[position() <= $k]
</syntaxhighlightpre>|-valign="top"
| '''Examples'''
|
{| width='100%'
|-valign="top"| width='120' | '''SignaturesSignature'''|{{Func|<pre>hof:top-k-with|( $seq input as item()*, $lt comparator as function(item(), item()) as xs:boolean, $k as xs:integer|) as item()*}}</pre>|-valign="top"
| '''Summary'''
|Returns the {{Code|$k}} items in {{Code|$seqinput}} that are greatest when sorted in the order of the ''less-than'' predicate {{Code|$ltcomparator}}. The function is a general version of {{CodeFunction||hof:top-k-by($seq, $sort-key, $k)}}.|-valign="top"
| '''Examples'''
|
|}
=IDsIdentity= ==hof:id== {| width='100%'|-| width='120' | '''Signatures'''|{{Func|hof:id|$expr as item()*|item()*}}|-| '''Summary'''|Returns its argument unchanged. This function isn't useful on its own, but can be used as argument to other higher-order functions.|-| '''Examples'''|* {{Code|hof:id(1 to 5)}} returns {{Code|1 2 3 4 5}}* With higher-order functions:<syntaxhighlight lang="xquery">let $sort := sort(?, (), hof:id#1)let $reverse-sort := sort(?, (), function($x) { -$x })return ( $sort((1, 5, 3, 2, 4)), '|', $reverse-sort((1, 5, 3, 2, 4)))</syntaxhighlight>returns: <code>1 2 3 4 5 | 5 4 3 2 1</code>|}
==hof:const==
{| width='100%'
|-valign="top"| width='120' | '''SignaturesSignature'''|{{Func|<pre>hof:const|( $expr input as item()*, $ignored ignore as item()*|) as item()*}}</pre>|-valign="top"
| '''Summary'''
|Returns its first argument unchanged and ignores the second. This function isn't isn’t useful on its own, but can be used as argument to other higher-order functions, e.g. , when a function combining two values is expected and one only wants to retain the left one.|-valign="top"
| '''Examples'''
|
* {{Code|hof:const(42, 1337)}} returns {{Code|42}}.
* With higher-order functions:
<syntaxhighlight pre lang="'xquery"'>
let $zip-sum := function($f, $seq1, $seq2) {
sum(for-each-pair($seq1, $seq2, $f))
$sum-left((1, 1, 1, 1, 1), 1 to 5)
)
</syntaxhighlightpre>
* Another use-case: When inserting a key into a map, {{Code|$f}} decides how to combine the new value with a possibly existing old one. {{Code|hof:const}} here means ignoring the old value, so that's normal insertion.
<syntaxhighlight pre lang="'xquery"'>
let $insert-with := function($f, $map, $k, $v) {
let $old := $map($k)
$ins($map, 'foo', 42)('foo')
)
</syntaxhighlightpre>
returns {{Code|3 42}}
|}
=Changelog=
;Version 11.0
* Removed: {{Code|hof:until}} (replaced with {{Code|fn:iterate-while}}, {{Code|hof:if}} (replaced with {{Code|fn:identity}}, {{Code|hof:drop-while}} (replaced with {{Code|fn:items-starting-where}}), {{Code|hof:take-while}} (replaced with {{Code|fn:items-before}})
;Version 9.5
* Added: {{Function||hof:drop-while}}
;Version 8.1
* Added: [[#hof:scan-left{{Function||hof:scan-left]]}}, [[#hof:take-while{{Function||hof:take-while]]}}
;Version 7.2
* Added: [[#hof:top-k-by{{Function||hof:top-k-by]]}}, [[#hof:top-k-with{{Function||hof:top-k-with]]}}
* Removed: hof:iterate
;Version 7.0
* module added