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.

+3


source to share


2 answers


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
          ]
      ]
  ]

      

  • 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

      

+2


source


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.

+2


source







All Articles