hof:until is gone?
Thursday, 28 March 2024 · 9 messages · last reply 29 Mar 2024
GS
Graydon Saunders28 Mar 2024 05:57 #
Hello --
So I'm trying to do this:
import module namespace xcs = "http://www.xcential.com/xquery/utils/script"
at "same-words-same-order-script.xqm";
(: we don't need xc computationally but there are external variables in
that namespace in scope :)
import module namespace xc = "http://www.xcential.com/xquery/utils" at
'same-words-same-order.xqm';
declare function xc:dropTableLines($in as node()*,$toggle as xs:boolean) as
node()* {
switch (true())
case empty($in) return ()
case starts-with(head($in),':stab') return
(<line/>,xc:dropTableLines(tail($in),true()))
case starts-with(head($in),':rtab') return
(<line/>,xc:dropTableLines(tail($in),false()))
case $toggle return (<line/>,xc:dropTableLines(tail($in),$toggle))
default return (head($in),xc:dropTableLines(tail($in),$toggle))
};
let $test as element() := <text>
<line></line>
<line></line>
<line>:stab</line>
<line>weasels</line>
<line>:stab</line>
<line>weasels</line>
<line></line>
<line></line>
<line>:rtab.</line>
<line></line>
<line>asparagus</line>
<line></line>
<line></line>
<line>:stab</line>
<line></line>
<line>weasels</line>
<line>:rtab.</line>
<line>asparagus</line>
<line>asparagus</line>
<line>asparagus</line>
<line>asparagus</line>
<line>asparagus</line>
<line>:stab</line>
<line>:stab</line>
<line></line>
<line></line>
<line>:stab</line>
<line></line>
<line>weasels</line>
<line></line>
<line>:rtab.</line>
<line></line>
<line>asparagus</line>
<line></line>
<line></line>
<line></line>
</text>
return element {'text'} {
xc:dropTableLines($test/line,false())
}Only at scale the stack blows up and I get the "try tail recursion?" suggestion. I would have tried hof:until for that, since I have to pass the current state of "are we dropping or not dropping intervening content?", but it looks like it's been removed? And the available hof functions in 4 look like they're strictly positional which is actively unhelpful in this case. (At least with whatever brain cells I currently have.)
What's the appropriate pattern for "process a sequence, toggling an action on or off based on the last member of the sequence we looked at?"
Thanks!
Graydon
LQ
Liam R. E. Quin28 Mar 2024 06:15 #
Quoted text · Graydon Saunders · 6 lines
On Wed, 2024-03-27 at 23:57 -0400, Graydon Saunders wrote: > > > What's the appropriate pattern for "process a sequence, toggling an > action on or off based on the last member of the sequence we looked > at?"
One approach might be to use fold-left on (1 to count($input)), with
$input in scope, so that you can write in the function argument to
fold-left things like
if (. eq 1) then $input[.]
else if (starts-with($input[. - 1], 'stab') then...etc
liam
--
Liam Quin, https://www.delightfulcomputing.com/
Available for XML/Document/Information Architecture/XSLT/
XSL/XQuery/Web/Text Processing/A11Y training, work & consulting. Barefoot Web-slave, antique illustrations: http://www.fromoldbooks.org
G
Graydon28 Mar 2024 07:03 #
Quoted text · Graydon Saunders · 14 lines
On Thu, Mar 28, 2024 at 12:14:45AM -0400, Liam R. E. Quin scripsit: > On Wed, 2024-03-27 at 23:57 -0400, Graydon Saunders wrote: > > What's the appropriate pattern for "process a sequence, toggling an > > action on or off based on the last member of the sequence we looked > > at?" > > One approach might be to use fold-left on (1 to count($input)), with > $input in scope, so that you can write in the function argument to > fold-left things like > > if (. eq 1) then $input[.] > else if (starts-with($input[. - 1], 'stab') then... > > etc
Yeah, I phrased that badly.
The cases are "I've seen A", "I've seen B (so forget I've seen A)", and "I haven't seen A". There can be multiple instances of A and B and most things are C and what happens to C depends on whether or not we've seen A some time previously in the sequence.
So the approach I want to take is to have a toggle that lets me set "Have seen A" as a boolean and pass it to the subsequent evaluations. This used to be something I could do with hof:until by passing a map as the parameter, but that's not looking like what the current hof functions support.
(I'm using hof:until in other places in this particular task so I'm worried that I'm going to have to replace that code when 11 comes out, too.)
Thanks!
Graydon
EK
This feels like an ideal use case for XSLT for-each-group—something that is challenging to implement in XQuery.
Cheers,
E.
_____________________________________________
Eliot Kimber
Sr Staff Content Engineer
Digital Content & Design
O: 512 554 9368
M: 512 554 9368
servicenow.com<https://www.servicenow.com>
LinkedIn<https://www.linkedin.com/company/servicenow> | Twitter<https://twitter.com/servicenow> | YouTube<https://www.youtube.com/user/servicenowinc> | Facebook<https://www.facebook.com/servicenow>
Quoted text · BaseX-Talk · 63 lines
From: BaseX-Talk <basex-talk-bounces at mailman.uni-konstanz.de> on behalf of Graydon Saunders <graydonish at gmail.com> Date: Thursday, March 28, 2024 at 7:43 AM To: BaseX <BaseX-Talk at mailman.uni-konstanz.de> Subject: [basex-talk] hof:until is gone? [External Email] Hello -- So I'm trying to do this: import module namespace xcs = "http://www.xcential.com/xquery/utils/script<http://www.xcential.com/xquery/utils/script>" at "same-words-same-order-script.xqm"; (: we don't need xc computationally but there are external variables in that namespace in scope :) import module namespace xc = "http://www.xcential.com/xquery/utils<http://www.xcential.com/xquery/utils>" at 'same-words-same-order.xqm'; declare function xc:dropTableLines($in as node()*,$toggle as xs:boolean) as node()* { switch (true()) case empty($in) return () case starts-with(head($in),':stab') return (<line/>,xc:dropTableLines(tail($in),true())) case starts-with(head($in),':rtab') return (<line/>,xc:dropTableLines(tail($in),false())) case $toggle return (<line/>,xc:dropTableLines(tail($in),$toggle)) default return (head($in),xc:dropTableLines(tail($in),$toggle)) }; let $test as element() := <text> <line></line> <line></line> <line>:stab</line> <line>weasels</line> <line>:stab</line> <line>weasels</line> <line></line> <line></line> <line>:rtab.</line> <line></line> <line>asparagus</line> <line></line> <line></line> <line>:stab</line> <line></line> <line>weasels</line> <line>:rtab.</line> <line>asparagus</line> <line>asparagus</line> <line>asparagus</line> <line>asparagus</line> <line>asparagus</line> <line>:stab</line> <line>:stab</line> <line></line> <line></line> <line>:stab</line> <line></line> <line>weasels</line> <line></line> <line>:rtab.</line> <line></line> <line>asparagus</line> <line></line> <line></line> <line></line> </text> return element {'text'} { xc:dropTableLines($test/line,false()) } Only at scale the stack blows up and I get the "try tail recursion?" suggestion. I would have tried hof:until for that, since I have to pass the current state of "are we dropping or not dropping intervening content?", but it looks like it's been removed? And the available hof functions in 4 look like they're strictly positional which is actively unhelpful in this case. (At least with whatever brain cells I currently have.) What's the appropriate pattern for "process a sequence, toggling an action on or off based on the last member of the sequence we looked at?" Thanks! Graydon
AB
Andy Bunce28 Mar 2024 14:06 #
hof:until is not gone, it is just hiding [1]
- In 10.7 it is there but undocumented in Wiki
- In BaseX 11 you need to use XQuery 4 fn:while-do⁺, fn:do-until⁺ [2]
Hope this helps
/Andy
[1]
https://www.mail-archive.com/basex-talk%40mailman.uni-konstanz.de/msg15596.html [2] https://help.basex.org/main/Higher-Order_Functions_Module
Quoted text · Eliot Kimber · 136 lines
On Thu, 28 Mar 2024 at 12:44, Eliot Kimber <eliot.kimber at servicenow.com> wrote: > This feels like an ideal use case for XSLT for-each-group—something that > is challenging to implement in XQuery. > > > > Cheers, > > > > E. > > > > _____________________________________________ > > *Eliot Kimber* > > Sr Staff Content Engineer > > Digital Content & Design > > O: 512 554 9368 > > M: 512 554 9368 > > servicenow.com <https://www.servicenow.com> > > LinkedIn <https://www.linkedin.com/company/servicenow> | Twitter > <https://twitter.com/servicenow> | YouTube > <https://www.youtube.com/user/servicenowinc> | Facebook > <https://www.facebook.com/servicenow> > > > > *From: *BaseX-Talk <basex-talk-bounces at mailman.uni-konstanz.de> on behalf > of Graydon Saunders <graydonish at gmail.com> > *Date: *Thursday, March 28, 2024 at 7:43 AM > *To: *BaseX <BaseX-Talk at mailman.uni-konstanz.de> > *Subject: *[basex-talk] hof:until is gone? > *[External Email]* > > > ------------------------------ > > Hello -- > > > > So I'm trying to do this: > > > > import module namespace xcs = "http://www.xcential.com/xquery/utils/script" > at "same-words-same-order-script.xqm"; > (: we don't need xc computationally but there are external variables in > that namespace in scope :) > import module namespace xc = "http://www.xcential.com/xquery/utils" at > 'same-words-same-order.xqm'; > > declare function xc:dropTableLines($in as node()*,$toggle as xs:boolean) > as node()* { > switch (true()) > case empty($in) return () > case starts-with(head($in),':stab') return > (<line/>,xc:dropTableLines(tail($in),true())) > case starts-with(head($in),':rtab') return > (<line/>,xc:dropTableLines(tail($in),false())) > case $toggle return (<line/>,xc:dropTableLines(tail($in),$toggle)) > default return (head($in),xc:dropTableLines(tail($in),$toggle)) > }; > > let $test as element() := <text> > <line></line> > <line></line> > <line>:stab</line> > <line>weasels</line> > <line>:stab</line> > <line>weasels</line> > <line></line> > <line></line> > <line>:rtab.</line> > <line></line> > <line>asparagus</line> > <line></line> > <line></line> > <line>:stab</line> > <line></line> > <line>weasels</line> > <line>:rtab.</line> > <line>asparagus</line> > <line>asparagus</line> > <line>asparagus</line> > <line>asparagus</line> > <line>asparagus</line> > <line>:stab</line> > <line>:stab</line> > <line></line> > <line></line> > <line>:stab</line> > <line></line> > <line>weasels</line> > <line></line> > <line>:rtab.</line> > <line></line> > <line>asparagus</line> > <line></line> > <line></line> > <line></line> > </text> > > return element {'text'} { > xc:dropTableLines($test/line,false()) > } > > > > Only at scale the stack blows up and I get the "try tail recursion?" > suggestion. I would have tried hof:until for that, since I have to pass the > current state of "are we dropping or not dropping intervening content?", > but it looks like it's been removed? And the available hof functions in 4 > look like they're strictly positional which is actively unhelpful in this > case. (At least with whatever brain cells I currently have.) > > > > What's the appropriate pattern for "process a sequence, toggling an action > on or off based on the last member of the sequence we looked at?" > > > > Thanks! > > Graydon >
G
Graydon28 Mar 2024 18:41 #
Hi Andy --
Quoted text · Andy Bunce · 3 lines
On Thu, Mar 28, 2024 at 01:06:10PM +0000, Andy Bunce scripsit: > hof:until is not gone, it is just hiding [1] > - In 10.7 it is there but undocumented in Wiki
Which is useful to know -- thank you! -- but makes me think a bunch of my production code is going to break hard when 11 is released. I would really like to avoid that. (In particular, the conversation with management about how much refactoring work will be required.)
Quoted text · Andy Bunce · 1 line
> - In BaseX 11 you need to use XQuery 4 fn:while-do⁺, fn:do-until⁺ [2]
It is very probably my brain, but I'm having trouble transposing hof:until to those functions. I can an implement what I want to do with hof:until like so:
declare function xc:dropTableLinesFancy($in as node()*) as node()* {
let $result as map(*) :=
hof:until(
(: are we done? (= we've run off the end of our list of nodes ) :)
function($m) {
empty($m?oldNodes)
},
(: create new list of line elements :)
function($m) {
map {
'oldNodes': tail($m?oldNodes),
'newNodes': ($m?newNodes,
switch (true())
case starts-with(head($m?oldNodes),':stab') return <line/>
case starts-with(head($m?oldNodes),':rtab') return <line/>
case $m?toggle return <line/>
default return head($m?oldNodes)
),
'toggle': (
switch (true())
case starts-with(head($m?oldNodes),':stab') return true()
case starts-with(head($m?oldNodes),':rtab') return false()
default return $m?toggle
)
}
},
(: initial input = we start with no new nodes :)
map { 'oldNodes': $in, 'newNodes': (), 'toggle': false() }
)
return $result?newNodes
};Anyone willing to provide an example of what that would look like in
fn:while-do or fn:do-until?
Thanks!
Graydon--
Graydon Saunders | graydonish at fastmail.com
Þæs oferéode, ðisses swá mæg.
-- Deor ("That passed, so may this.")
CG
Christian Grün28 Mar 2024 19:33 #
Hi Graydon,
Folks tell us it’s time to stop delaying BaseX 11… We’re trying hard.
The good news: The only difference between hof:until [1] and
fn:do-until [2] is the order of parameters. The following queries will do the same thing:hof:until(function($a) { $a > 16 }, function($a) { $a * 2 }, 1)
do-until(1, function($a) { $a * 2 }, function($a) { $a > 16 })With
fn:do-until, the input has moved to the first position, the action comes second and the predicate comes last.
Hope this helps,
Christian
[1] https://files.basex.org/releases/10.0/BaseX100.pdf
[2]
https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-do-untilQuoted text · Graydon · 62 lines
On Thu, Mar 28, 2024 at 5:41 PM Graydon <graydonish at gmail.com> wrote: > Hi Andy -- > > On Thu, Mar 28, 2024 at 01:06:10PM +0000, Andy Bunce scripsit: > > hof:until is not gone, it is just hiding [1] > > - In 10.7 it is there but undocumented in Wiki > > Which is useful to know -- thank you! -- but makes me think a bunch of > my production code is going to break hard when 11 is released. I would > really like to avoid that. (In particular, the conversation with > management about how much refactoring work will be required.) > > > - In BaseX 11 you need to use XQuery 4 fn:while-do⁺, fn:do-until⁺ [2] > > It is very probably my brain, but I'm having trouble transposing > hof:until to those functions. I can an implement what I want to do with > hof:until like so: > > declare function xc:dropTableLinesFancy($in as node()*) as node()* { > let $result as map(*) := > hof:until( > (: are we done? (= we've run off the end of our list of nodes ) :) > function($m) { > empty($m?oldNodes) > }, > (: create new list of line elements :) > function($m) { > map { > 'oldNodes': tail($m?oldNodes), > 'newNodes': ($m?newNodes, > switch (true()) > case starts-with(head($m?oldNodes),':stab') return <line/> > case starts-with(head($m?oldNodes),':rtab') return <line/> > case $m?toggle return <line/> > default return head($m?oldNodes) > ), > 'toggle': ( > switch (true()) > case starts-with(head($m?oldNodes),':stab') return true() > case starts-with(head($m?oldNodes),':rtab') return false() > default return $m?toggle > ) > } > }, > (: initial input = we start with no new nodes :) > map { 'oldNodes': $in, 'newNodes': (), 'toggle': false() } > ) > return $result?newNodes > }; > > Anyone willing to provide an example of what that would look like in > fn:while-do or fn:do-until? > > Thanks! > Graydon > > > -- > Graydon Saunders | graydonish at fastmail.com > Þæs oferéode, ðisses swá mæg. > -- Deor ("That passed, so may this.") >
AB
Andy Bunce28 Mar 2024 23:49 #
Hi Christian,
That hof:until update recipe is very helpful.
From my occasional reads of qt4 feeds, I guess Graydon's function might look something like the below
I like the look of the upcoming V4.0 features.
/Andy
declare function local:dropTableLinesFancy($in as node()*) as node()* {
do-until(
(: initial input = we start with no new nodes :)
map { 'oldNodes': $in, 'newNodes': (), 'toggle': false()
},
fn($m){ map {
'oldNodes': tail($m?oldNodes),
'newNodes': ($m?newNodes,
switch {
case starts-with(head($m?oldNodes),':stab')
return <line/>
case starts-with(head($m?oldNodes),':rtab')
return <line/>
case $m?toggle return <line/>
default return head($m?oldNodes)
}),
'toggle': switch {
case starts-with(head($m?oldNodes),':stab') return
true()
case starts-with(head($m?oldNodes),':rtab') return
false()
default return $m?toggle
}
}
},
(: are we done? (= we've run off the end of our list of nodes ) :)
fn($m){ empty($m?oldNodes) }
)?newNodes
};Quoted text · Christian Grün · 89 lines
On Thu, 28 Mar 2024 at 18:34, Christian Grün <christian.gruen at gmail.com> wrote: > Hi Graydon, > > Folks tell us it’s time to stop delaying BaseX 11… We’re trying hard. > > The good news: The only difference between hof:until [1] and fn:do-until > [2] is the order of parameters. The following queries will do the same > thing: > > hof:until(function($a) { $a > 16 }, function($a) { $a * 2 }, 1) > do-until(1, function($a) { $a * 2 }, function($a) { $a > 16 }) > > With fn:do-until, the input has moved to the first position, the action > comes second and the predicate comes last. > > Hope this helps, > Christian > > [1] https://files.basex.org/releases/10.0/BaseX100.pdf > [2] > https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-do-until > > > > On Thu, Mar 28, 2024 at 5:41 PM Graydon <graydonish at gmail.com> wrote: > >> Hi Andy -- >> >> On Thu, Mar 28, 2024 at 01:06:10PM +0000, Andy Bunce scripsit: >> > hof:until is not gone, it is just hiding [1] >> > - In 10.7 it is there but undocumented in Wiki >> >> Which is useful to know -- thank you! -- but makes me think a bunch of >> my production code is going to break hard when 11 is released. I would >> really like to avoid that. (In particular, the conversation with >> management about how much refactoring work will be required.) >> >> > - In BaseX 11 you need to use XQuery 4 fn:while-do⁺, fn:do-until⁺ [2] >> >> It is very probably my brain, but I'm having trouble transposing >> hof:until to those functions. I can an implement what I want to do with >> hof:until like so: >> >> declare function xc:dropTableLinesFancy($in as node()*) as node()* { >> let $result as map(*) := >> hof:until( >> (: are we done? (= we've run off the end of our list of nodes ) :) >> function($m) { >> empty($m?oldNodes) >> }, >> (: create new list of line elements :) >> function($m) { >> map { >> 'oldNodes': tail($m?oldNodes), >> 'newNodes': ($m?newNodes, >> switch (true()) >> case starts-with(head($m?oldNodes),':stab') return <line/> >> case starts-with(head($m?oldNodes),':rtab') return <line/> >> case $m?toggle return <line/> >> default return head($m?oldNodes) >> ), >> 'toggle': ( >> switch (true()) >> case starts-with(head($m?oldNodes),':stab') return true() >> case starts-with(head($m?oldNodes),':rtab') return false() >> default return $m?toggle >> ) >> } >> }, >> (: initial input = we start with no new nodes :) >> map { 'oldNodes': $in, 'newNodes': (), 'toggle': false() } >> ) >> return $result?newNodes >> }; >> >> Anyone willing to provide an example of what that would look like in >> fn:while-do or fn:do-until? >> >> Thanks! >> Graydon >> >> >> -- >> Graydon Saunders | graydonish at fastmail.com >> Þæs oferéode, ðisses swá mæg. >> -- Deor ("That passed, so may this.") >> >
G
Quoted text · Christian Grün · 2 lines
On Thu, Mar 28, 2024 at 07:33:51PM +0100, Christian Grün scripsit: > Hi Graydon,
Hi Christian,
Quoted text · Christian Grün · 1 line
> Folks tell us it’s time to stop delaying BaseX 11… We’re trying hard.
I am generally in favour of 11, or I suppose 4, depending.
Quoted text · Christian Grün · 9 lines
> The good news: The only difference between hof:until [1] and fn:do-until > [2] is the order of parameters. The following queries will do the same > thing: > > hof:until(function($a) { $a > 16 }, function($a) { $a * 2 }, 1) > do-until(1, function($a) { $a * 2 }, function($a) { $a > 16 }) > > With fn:do-until, the input has moved to the first position, the action > comes second and the predicate comes last.
Thank you!
That is helpful indeed. (Definitely my brain.)
And I can confirm it works for me with the current 11 beta.
-- Graydon
Written for BaseX 10.7. BaseX may have changed since: please check the current documentation.
⚡Generated with XQuery