在 XPath 3.1 的 array:filter 中,我可以指定一个过滤函数,它接受多个参数(即一个值来*反对*测试项目)吗?
In XPath 3.1's array:filter, can I specify a filtering function that takes more than one argument (i.e. a value to test the item *against*)?
我想根据其中一个键的值过滤一组地图。问题似乎是 array:filter()
的第二个参数是一个仅接受单个项目参数的函数:根据 XPath and XQuery Operators 3.1 specs.
array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*)
但是当我执行 array:filter($routingTable, function ($i) {$i?input ne $wid})
时,由于 $wid
在此调用之前的某处被定义,此变量未被传递到函数中并且比较错过了关键条目。 (在调用 array:filter
之前用调试输出检查 $wid
的值确认它具有正确的值。检查匿名函数内部的 $i?input
也确认了这个值。但是检查 $wid
里面匿名函数让它看起来是空的。)
所以我想也许我需要将要比较的值作为第二个参数传递给过滤函数,但是当我这样做时 array:filter($routingTable, function ($i, $wid) {etc...
,我得到了 java.lang.ArrayIndexOutOfBoundsException
错误。我认为这是由于函数的过多参数造成的。我该怎么办?
就其价值而言,我的 XQuery 处理器是 eXist-db (6.1.0),这里有更完整的代码(有问题的调用(我假设)是最后一个函数的第二行):
(: for a sequence of nodes, I build maps and put them into an array. that is my routing table that is then posted :)
declare function my:createRoutes($wid as xs:string) {
let $index := doc($config:index-root || "/" || $wid || ".xml")/my:index
let $routingTable := array{fn:for-each($index//my:node, function($k) {my:buildRoutingInfoNode($wid, $k)} )}
return my:postRoutingTable($routingTable)
};
(: helper function to create a map from a node :)
declare function my:buildRoutingInfoNode($wid as xs:string, $item as element(my:node)) {
map { "input" : concat($wid, ":", $item/@citeID/string()), "outputs" : array { ( $item/@crumb/string(), 'yes' ) } }
};
(: here the routing table is posted. However, if the entries are already present in the "live" table, I need to clean them from there first :)
declare function my:postRoutingTable($routes as array(*)) as xs:integer {
if (array:size($routes) = 0) then
0
else
let $testmap := $routes?1 (: okay, that's a bit cheap: I just check the first of the new entries. :)
let $src := $testmap?input
let $dest := $testmap?outputs
return if (not(my:isInRoutingTable($src, $dest))) then
... post via http request ...
else (: At least one key is already present, need to clean routing table for $wid first :)
let $wid := substring-before($src, ":")
let $cleanStatus := my:cleanRoutingTable($wid)
return if ($cleanStatus ge 0) then
my:postRoutingTable($routes) (: retry ... :)
else
-1 (: cleaning failed ... :)
};
(: remove all entries about the $wid (so that I can add them again) :)
declare function my:cleanRoutingTable($wid as xs:string) as xs:integer {
let $routingTable := my:getRoutingTable() (: get "live" table :)
let $cleanedRT := array:filter($routingTable, function ($i) {
substring($i?input, 1, 5) ne $wid
}) (: remove all entries concerning the to-be-posted $wid :)
let $deleteStatus := my:deleteRoutingTable() (: drop the complete live table :)
return if (array:size($cleanedRT) > 0) then (: if after removing entries, anything is left of the original "live" table, :)
my:postRoutingTable($cleanedRT) (: then re-post this "cleaned" table :)
else -1
};
从表面上看,这看起来像是一个 eXist-db 错误。变量 $wid
是匿名函数闭包的一部分,它的值应该是可访问的;您的代码看起来不错——尽管没有完整的重现(源文档和预期结果)我还没有在其他地方测试过它。
将第二个变量传递给过滤函数
要动态过滤项目列表,通常需要传递第二个动态参数。
读取作用域变量的 lambda 或匿名函数的常用方法是:
let $upper-bound := 4 (: this might be read from user input :)
return filter(1 to 9, function ($item) {
$item < $upper-bound
})
通过一些元编程并利用参数占位符 ?
这也可以重写为
declare function local:filter ($item, $upper-bound) {
$item < $upper-bound
};
let $upper-bound := 4
return filter(1 to 9, local:filter(?, $upper-bound))
local:filter(?, $upper-bound)
将 return 一个元数为 1 的函数,这正是 fn:filter
和 array:filter
所期望的。
主要好处是它允许重用过滤器函数(这里 local:filter
)。
现在也可以在导入的模块中定义此函数。
我想根据其中一个键的值过滤一组地图。问题似乎是 array:filter()
的第二个参数是一个仅接受单个项目参数的函数:根据 XPath and XQuery Operators 3.1 specs.
array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*)
但是当我执行 array:filter($routingTable, function ($i) {$i?input ne $wid})
时,由于 $wid
在此调用之前的某处被定义,此变量未被传递到函数中并且比较错过了关键条目。 (在调用 array:filter
之前用调试输出检查 $wid
的值确认它具有正确的值。检查匿名函数内部的 $i?input
也确认了这个值。但是检查 $wid
里面匿名函数让它看起来是空的。)
所以我想也许我需要将要比较的值作为第二个参数传递给过滤函数,但是当我这样做时 array:filter($routingTable, function ($i, $wid) {etc...
,我得到了 java.lang.ArrayIndexOutOfBoundsException
错误。我认为这是由于函数的过多参数造成的。我该怎么办?
就其价值而言,我的 XQuery 处理器是 eXist-db (6.1.0),这里有更完整的代码(有问题的调用(我假设)是最后一个函数的第二行):
(: for a sequence of nodes, I build maps and put them into an array. that is my routing table that is then posted :)
declare function my:createRoutes($wid as xs:string) {
let $index := doc($config:index-root || "/" || $wid || ".xml")/my:index
let $routingTable := array{fn:for-each($index//my:node, function($k) {my:buildRoutingInfoNode($wid, $k)} )}
return my:postRoutingTable($routingTable)
};
(: helper function to create a map from a node :)
declare function my:buildRoutingInfoNode($wid as xs:string, $item as element(my:node)) {
map { "input" : concat($wid, ":", $item/@citeID/string()), "outputs" : array { ( $item/@crumb/string(), 'yes' ) } }
};
(: here the routing table is posted. However, if the entries are already present in the "live" table, I need to clean them from there first :)
declare function my:postRoutingTable($routes as array(*)) as xs:integer {
if (array:size($routes) = 0) then
0
else
let $testmap := $routes?1 (: okay, that's a bit cheap: I just check the first of the new entries. :)
let $src := $testmap?input
let $dest := $testmap?outputs
return if (not(my:isInRoutingTable($src, $dest))) then
... post via http request ...
else (: At least one key is already present, need to clean routing table for $wid first :)
let $wid := substring-before($src, ":")
let $cleanStatus := my:cleanRoutingTable($wid)
return if ($cleanStatus ge 0) then
my:postRoutingTable($routes) (: retry ... :)
else
-1 (: cleaning failed ... :)
};
(: remove all entries about the $wid (so that I can add them again) :)
declare function my:cleanRoutingTable($wid as xs:string) as xs:integer {
let $routingTable := my:getRoutingTable() (: get "live" table :)
let $cleanedRT := array:filter($routingTable, function ($i) {
substring($i?input, 1, 5) ne $wid
}) (: remove all entries concerning the to-be-posted $wid :)
let $deleteStatus := my:deleteRoutingTable() (: drop the complete live table :)
return if (array:size($cleanedRT) > 0) then (: if after removing entries, anything is left of the original "live" table, :)
my:postRoutingTable($cleanedRT) (: then re-post this "cleaned" table :)
else -1
};
从表面上看,这看起来像是一个 eXist-db 错误。变量 $wid
是匿名函数闭包的一部分,它的值应该是可访问的;您的代码看起来不错——尽管没有完整的重现(源文档和预期结果)我还没有在其他地方测试过它。
将第二个变量传递给过滤函数
要动态过滤项目列表,通常需要传递第二个动态参数。
读取作用域变量的 lambda 或匿名函数的常用方法是:
let $upper-bound := 4 (: this might be read from user input :)
return filter(1 to 9, function ($item) {
$item < $upper-bound
})
通过一些元编程并利用参数占位符 ?
这也可以重写为
declare function local:filter ($item, $upper-bound) {
$item < $upper-bound
};
let $upper-bound := 4
return filter(1 to 9, local:filter(?, $upper-bound))
local:filter(?, $upper-bound)
将 return 一个元数为 1 的函数,这正是 fn:filter
和 array:filter
所期望的。
主要好处是它允许重用过滤器函数(这里 local:filter
)。
现在也可以在导入的模块中定义此函数。