MarkLogic XQuery 尾调用优化
MarkLogic XQuery tail call optimization
我重写了一个溢出堆栈的递归函数以使用累加器,因为我认为 ML 正在优化这种模式。但是,我仍然收到堆栈溢出异常。这个函数在尾调用优化方面有什么问题吗?
xquery version "1.0-ml";
declare variable $BATCH-DURATION := xs:dayTimeDuration("P5D");
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
) as xs:dateTime+
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then $acc
else local:start-times($datetime-start + $BATCH-DURATION, $datetime-end, ($acc, $datetime-start))
};
local:start-times(xs:dateTime('1800-01-01T17:45:42'), xs:dateTime('2017-10-10T17:45:42'), ())[last()]
我相信尾调用优化只有在函数的 return 值是无类型的情况下才有效。
即试:
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
)
{
希望对您有所帮助。
除了 Erik (@ehennum) 的中肯回答之外,关于代码可能还有更多要说的。即使需要递归,也可能有更聪明的做事方式。这里有一些带有指标的替代算法:
xquery version "1.0-ml";
declare variable $BATCH-DURATION := xs:dayTimeDuration("P5D");
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
)
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then $acc
else local:start-times($datetime-start + $BATCH-DURATION, $datetime-end, ($acc, $datetime-start))
};
declare function local:start-times2(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime
)
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then ()
else
let $tick := $datetime-start + $BATCH-DURATION
return ($tick, local:start-times2($tick, $datetime-end))
};
let $datetime-start := xs:dateTime('1800-01-01T17:45:42')
let $datetime-end := xs:dateTime('2017-10-10T17:45:42')
return (
(: original recursive method :)
let $start-time := xdmp:elapsed-time()
return (
local:start-times($datetime-start, $datetime-end, ())[last()],
xdmp:elapsed-time() - $start-time
),
(: more efficient recursive methode :)
let $start-time := xdmp:elapsed-time()
return (
local:start-times2($datetime-start, $datetime-end)[last()],
xdmp:elapsed-time() - $start-time
),
(: non-recursive method:)
let $start-time := xdmp:elapsed-time()
let $datetime-closest :=
$datetime-start + (fn:floor(($datetime-end - $datetime-start) div $BATCH-DURATION) * $BATCH-DURATION)
return (
(: handle edge case :)
if ($datetime-closest eq $datetime-end) then
$datetime-closest - $BATCH-DURATION
else
$datetime-closest,
xdmp:elapsed-time() - $start-time
)
)
HTH!
我重写了一个溢出堆栈的递归函数以使用累加器,因为我认为 ML 正在优化这种模式。但是,我仍然收到堆栈溢出异常。这个函数在尾调用优化方面有什么问题吗?
xquery version "1.0-ml";
declare variable $BATCH-DURATION := xs:dayTimeDuration("P5D");
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
) as xs:dateTime+
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then $acc
else local:start-times($datetime-start + $BATCH-DURATION, $datetime-end, ($acc, $datetime-start))
};
local:start-times(xs:dateTime('1800-01-01T17:45:42'), xs:dateTime('2017-10-10T17:45:42'), ())[last()]
我相信尾调用优化只有在函数的 return 值是无类型的情况下才有效。
即试:
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
)
{
希望对您有所帮助。
除了 Erik (@ehennum) 的中肯回答之外,关于代码可能还有更多要说的。即使需要递归,也可能有更聪明的做事方式。这里有一些带有指标的替代算法:
xquery version "1.0-ml";
declare variable $BATCH-DURATION := xs:dayTimeDuration("P5D");
declare function local:start-times(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime,
$acc as xs:dateTime*
)
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then $acc
else local:start-times($datetime-start + $BATCH-DURATION, $datetime-end, ($acc, $datetime-start))
};
declare function local:start-times2(
$datetime-start as xs:dateTime,
$datetime-end as xs:dateTime
)
{
if (($datetime-end - $datetime-start) le $BATCH-DURATION) then ()
else
let $tick := $datetime-start + $BATCH-DURATION
return ($tick, local:start-times2($tick, $datetime-end))
};
let $datetime-start := xs:dateTime('1800-01-01T17:45:42')
let $datetime-end := xs:dateTime('2017-10-10T17:45:42')
return (
(: original recursive method :)
let $start-time := xdmp:elapsed-time()
return (
local:start-times($datetime-start, $datetime-end, ())[last()],
xdmp:elapsed-time() - $start-time
),
(: more efficient recursive methode :)
let $start-time := xdmp:elapsed-time()
return (
local:start-times2($datetime-start, $datetime-end)[last()],
xdmp:elapsed-time() - $start-time
),
(: non-recursive method:)
let $start-time := xdmp:elapsed-time()
let $datetime-closest :=
$datetime-start + (fn:floor(($datetime-end - $datetime-start) div $BATCH-DURATION) * $BATCH-DURATION)
return (
(: handle edge case :)
if ($datetime-closest eq $datetime-end) then
$datetime-closest - $BATCH-DURATION
else
$datetime-closest,
xdmp:elapsed-time() - $start-time
)
)
HTH!