Breaking a recursive function
I am walking through a set of nested blocks and want to stop walking when I find the value I am looking for.
For reasons beyond the scope of this question, I cannot use PARSE for this particular problem and not use FOREACH as a looper:
walk: func [series [block!] criteria [block!]][
use [value] compose/deep [
while [not tail? series][
value: pick series 1
either block? value [
walk value criteria
][
(to paren! criteria)
]
series: next series
]
]
]
I would like to break through if I find that specific meaning.
walk [a [b c [d e] f] g] [if value = 'e [return value]]
; returns 'e
However, I would also like to perform operations that do not fail:
walk [a [b c [d e] f] g] [
collect [if find [c e] value [keep value]]
]
; returns [c e]
Would love to try and solve this for any of the Rebol variants including Red. Any thoughts on efficiency (the reason I am using a block instead of a function) etc. are also welcome.
source to share
The combination of features I was looking for is CATCH / THROW. Once again, using this function:
walk: func [series [block!] criteria [block!]][
use [value] compose/deep [
while [not tail? series][
value: pick series 1
either block? value [
walk value criteria
][
(to paren! criteria)
]
series: next series
]
]
]
I can just wrap it like this:
catch [walk [a [b c [d e] f] g] [if value = 'e [throw value]]]
; returns 'e
Some notes
- I want the function to return NONE if there is no match
I will only have WALK return NONE (I use ALSO just to avoid leaving an awkward trailing none
):
walk: func [series [block!] criteria [block!]][
also none use [value] compose/deep [
while [not tail? series][
value: pick series 1
either block? value [
walk value criteria
][
(to paren! criteria)
]
series: next series
]
]
]
- red has no USE function
This introduces a complication as I want to bind the block to the word VALUE. If I had to rewrite the function like this:
walk: func [series [block!] criteria [block!] /local value][
do bind compose/deep [
while [not tail? series][
value: pick series 1
either block? value [
walk value criteria
][
(to paren! criteria)
]
series: next series
]
] 'value
]
Then it also binds the same block with the words SERIES and CRITERIA, which override the binding of any such words from the calling context, for example:
walk [some values][series: none probe value] ; results in error
source to share
This version avoids binding everything except VALUE and works in Red 0.6.3 and Rebol2:
walk: func [series [block!] criteria [block!]][
also none do bind compose/deep [
while [not tail? series] [
value: pick series 1
either block? value [
walk value criteria
] [
(to paren! criteria)
]
series: next series
]
]
context [value: none]
]
(Comments on how this implementation differs from what USE does are welcome.)
And yes, it doesn't work on Rebol3 Alpha. But both of them are with USE. I think this is a problem with THROW.
source to share