Higher-Order Functions Module

From BaseX Documentation
Revision as of 18:31, 1 December 2023 by CG (talk | contribs) (Text replacement - "</syntaxhighlight>" to "</pre>")
Jump to navigation Jump to search

This XQuery Module adds some useful higher-order functions, additional to the Higher-Order Functions provided by the official specification.

With Version 11, many functions have been removed in favor of new features of XQuery 4:

BaseX 10 XQuery 4
hof:drop-while fn:items-starting-where
hof:id fn:identity
hof:until fn:iterate-while
hof:take-while fn:items-before

Conventions

All functions in this module are assigned to the http://basex.org/modules/hof namespace, which is statically bound to the hof prefix.

Loops

hof:fold-left1

Signature
hof:fold-left1(
  $input   as item()+,
  $action  as function(item()*, item()) as item()*
) as item()*
Summary Works the same as fn:fold-left, but does not need a seed, because the sequence must be non-empty.
Examples
  • hof:fold-left1(1 to 10, function($a, $b) { $a + $b }) returns 55.
  • hof:fold-left1((), function($a, $b) { $a + $b }) throws XPTY0004, because $seq has to be non-empty.

hof:scan-left

Signature
hof:scan-left(
  $input   as item()*,
  $zero    as item()*,
  $action  as function(item()*, item()) as item()*
) as item()*
Summary This function is similar to fn:fold-left, but it returns a list of successive reduced values from the left. It is equivalent to:

<syntaxhighlight lang="xquery"> declare function hof:scan-left($input, $acc, $action) {

 if(empty($input)) then $acc else (
   $acc,
   hof:scan-left(tail($input), $action($acc, head($input)), $action)
 )

};

Examples
  • Returns triangular numbers:

<syntaxhighlight lang="xquery"> hof:scan-left(1 to 10, 0, function($a, $b) { $a + $b })

Sorting

hof:top-k-by

Signature
hof:top-k-by(
  $input  as item()*,
  $key    as function(item()) as item(),
  $k      as xs:integer
) as item()*
Summary Returns the $k items in $input that are greatest when sorted by the result of $key applied to the item. The function is a much more efficient implementation of the following scheme:

<syntaxhighlight lang="xquery"> (for $item in $input

order by $key($item) descending
return $item

)[position() <= $k]

Examples
  • hof:top-k-by(1 to 1000, hof:id#1, 5) returns 1000 999 998 997 996
  • hof:top-k-by(1 to 1000, function($x) { -$x }, 3) returns 1 2 3
  • hof:top-k-by(<x a='1' b='2' c='3'/>/@*, xs:integer#1, 2)/node-name() returns c b

hof:top-k-with

Signature
hof:top-k-with(
  $input       as item()*,
  $comparator  as function(item(), item()) as xs:boolean,
  $k           as xs:integer
) as item()*
Summary Returns the $k items in $input that are greatest when sorted in the order of the less-than predicate $comparator. The function is a general version of hof:top-k-by.
Examples
  • hof:top-k-with(1 to 1000, function($a, $b) { $a lt $b }, 5) returns 1000 999 998 997 996
  • hof:top-k-with(-5 to 5, function($a, $b) { abs($a) gt abs($b) }, 5) returns 0 1 -1 2 -2

Identity

hof:const

Signature
hof:const(
  $input   as item()*,
  $ignore  as item()*
) as item()*
Summary Returns its first argument unchanged and ignores the second. This function 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.
Examples
  • hof:const(42, 1337) returns 42.
  • With higher-order functions:

<syntaxhighlight lang="xquery"> let $zip-sum := function($f, $seq1, $seq2) {

 sum(for-each-pair($seq1, $seq2, $f))

} let $sum-all := $zip-sum(function($a, $b) { $a + $b }, ?, ?) let $sum-left := $zip-sum(hof:const#2, ?, ?) return (

 $sum-all((1, 1, 1, 1, 1), 1 to 5),
 $sum-left((1, 1, 1, 1, 1), 1 to 5)

)

  • Another use-case: When inserting a key into a map, $f decides how to combine the new value with a possibly existing old one. hof:const here means ignoring the old value, so that's normal insertion.

<syntaxhighlight lang="xquery"> let $insert-with := function($f, $map, $k, $v) {

 let $old := $map($k)
 let $new := if($old) then $f($v, $old) else $v
 return map:merge(($map, map:entry($k, $new)))

} let $map := map { 'foo': 1 } let $add := $insert-with(function($a, $b) { $a + $b }, ?, ?, ?) let $ins := $insert-with(hof:const#2, ?, ?, ?) return (

 $add($map, 'foo', 2)('foo'),
 $ins($map, 'foo', 42)('foo')

)

returns 3 42

Changelog

Version 11.0
  • Removed: hof:until (replaced with fn:iterate-while, hof:if (replaced with fn:identity, hof:drop-while (replaced with fn:items-starting-where), hof:take-while (replaced with fn:items-before)
Version 9.5
Version 8.1
Version 7.2
Version 7.0
  • module added