World: r4wp
[Rebol School] REBOL School
older newer | first last |
Steeve 3-Oct-2012 [1077] | I don't think you need 'with at all, you can throw back the new parameters as an argument of the throw function, like: >> throw/name reduce args 'recurse |
Ladislav 3-Oct-2012 [1078x2] | also, there is a problem with the result I think |
(I mean the function result) | |
MarcS 3-Oct-2012 [1080x2] | steeve: ooh, good point |
ladislav: ah yes, there is | |
Steeve 3-Oct-2012 [1082] | Here is a version with no locals, no temporary context, no shit and not tested ;-) rfunc: func [spec body][ func spec compose/deep [ forever [ set [(spec)] catch/name [ return (func spec body) (spec) ] 'recur ] ] ] recur: func [args][throw/name reduce args 'recur] |
MarcS 3-Oct-2012 [1083x2] | wow |
works well - full thing here: http://0branch.com/highlight/snippets/rfunc4.r | |
Steeve 3-Oct-2012 [1085x3] | to return a value and stop the function you need to use 'return instead of 'break |
I think | |
recur (Must be in tail position) -> not needed anymore | |
MarcS 3-Oct-2012 [1088] | well, it has to be a tail call |
Steeve 3-Oct-2012 [1089] | it's a tail call but it can be anywhere in the function body |
MarcS 3-Oct-2012 [1090x2] | so you couldn't do x + recur [ y ] |
sounds like we mean different things by 'tail position' | |
Steeve 3-Oct-2012 [1092x3] | it seems |
Yep, it seems | |
Yep, it seems | |
MarcS 3-Oct-2012 [1095] | i mean this: http://en.wikipedia.org/wiki/Tail_call |
Steeve 3-Oct-2012 [1096x2] | (lag) |
It's tail call recursion yes, but tail position usualy means "physically placed" at the tail (in our context, at the end of the body of the function) Anyway... | |
MarcS 3-Oct-2012 [1098] | not be pedantic, but i linked to that for the opening two sentences: In computer science, a tail call is a subroutine call that happens inside another procedure as its final action; it may produce a return value which is then immediately returned by the calling procedure. The call site is then said to be in tail position, i.e. at the end of the calling procedure. |
Steeve 3-Oct-2012 [1099] | Okay T_T |
MarcS 3-Oct-2012 [1100x2] | i've never heard tail position refer to last 'physical place' |
anyway, not important | |
Steeve 3-Oct-2012 [1102] | I just decided it would be a new paradigm :-) |
MarcS 3-Oct-2012 [1103x2] | heh, good stuff |
thanks again for the help | |
Steeve 3-Oct-2012 [1105] | No prob I was lazy on my couch |
Ladislav 3-Oct-2012 [1106] | Marc, to no pollute this group I posted a private conversation mentioning how to define a TAIL-CALL function using the function spec and how to handle the situation even if the function spec is "more complicated" than just a block of words. |
Steeve 3-Oct-2012 [1107] | It's Rebol School group here. Why would your post be a pollution ? I don't get it |
Ladislav 3-Oct-2012 [1108] | It is a bit long to my taste, but I can repost here if you prefer. Also, BTW, welcome to REBOL, Marc |
Steeve 3-Oct-2012 [1109] | Yes wellcome Marc. (Not really a beginner though) Yes Ladislav I've nothing against a long post here since it's related to the topic, or if you prefer you can use a copy/past service in the cloud. |
MarcS 3-Oct-2012 [1110] | thanks guys |
Ladislav 3-Oct-2012 [1111] | OK, this is the long version: tail-func: func [ { Define a recursive user function with the supplied SPEC and BODY. The function can use a special TAIL-CALL local function to perform a tail-recursive function call. } [catch] spec [block!] {Help string (opt) followed by arg words (and opt type and string)} body [block!] {The body block of the function} /local the-function tail-call context-word ] [ ; define a new 'tail-call local variable tail-call: use [tail-call] ['tail-call] ; bind the given BODY to "know" the 'tail-call variable body: bind/copy body tail-call ; find a local word in SPEC context-word: find spec word! if context-word [context-word: first context-word] ; define the TAIL-CALL function set tail-call func spec compose [ ( either context-word [ ; set parameters to the new arguments compose [set parameters values? (context-word)] ] [[]] ) throw/name none 'tail-call ] ; define the function the-function: throw-on-error [ func spec compose/deep [ (either context-word [context-word] [[]]) while [true] [ catch/name [ return do [(body)] ] 'tail-call ] ] ] if context-word [ ; get the function context context-word: bind? first second :the-function ; replace the context word in the function body by NONE change second :the-function none ; adjust the TAIL-CALL body ; replace the 'parameters word change/only at second get tail-call 2 bind first context-word context-word ] :the-function ] values?: func ['word] [second bind? word] |
Steeve 3-Oct-2012 [1112] | Hum Ok, I see the >> return do [(body)] as a nice optimization of my code. But for the rest, I'm not sure... |
Ladislav 3-Oct-2012 [1113] | example: safe: tail-func [x] [ if x > 20000 [print x exit] tail-call x + 1 ] |
Steeve 3-Oct-2012 [1114x3] | yeah I see that the purpose is to get ride of the block passing style of the arguments, but it looks not anymore as simple |
I think I included all your modifications Ladislav but shortly :-) rfunc: [spec body /local args][ args: to-block form first ( do second func spec compose [bind? (to-lit-word first find spec word!)] ) funct spec compose/deep [ recur: func spec [ throw/name reduce [(args)] 'recur ] forever [ set [(args)] catch/name [ return do [(body)] ] 'recur ] ] ] | |
I used a trick not well known. >> do second function! allow to execute a function without having to pass its arguments. | |
Kaj 3-Oct-2012 [1117] | Neat |
Steeve 3-Oct-2012 [1118x3] | Mmmmh, the function recur could be defined outside |
Should do the trick: rfunc: [spec body /local args][ args: to-block form first do second func spec compose [bind? (to-lit-word first find spec word!)] funct spec compose/deep [ recur: quote (func spec compose/deep [throw/name reduce [(args)] 'recur]) forever [ set [(args)] catch/name [return do [(body)]] 'recur ] ] ] | |
Notice the weird sequence >> to-block form first object! That is because the local context returned from a function is not well formed. If the specs of the function are [a [block!] /local b], it will return a strange objetc! where: >> first object! == [a /local b] So to correct its format, I do >> to-block form first object == [a local b] | |
Gregg 3-Oct-2012 [1121x2] | Marc, a simple naming question. Why did you choose 'recur instead of 'recurse? |
Ah, I love how AltMe optimizes code. :-) Thanks all. Very fun chat on this. | |
Steeve 3-Oct-2012 [1123] | I think Marc like to tease us with his naming convention |
Gregg 3-Oct-2012 [1124] | Now, there is this gloal RECUR func that is only usable inside RFUNC created funcs. While I'm still not very fond of the special KEEP func in COLLECT, should RECURSE be hidden/protected somehow? |
Steeve 3-Oct-2012 [1125] | Well it could be protected but see, you can also redefine standard words in every functions, at your own risk |
Gregg 3-Oct-2012 [1126] | Of course. For this, I'm also OK with the doc-string making the use clear. e.g. adding ONLY or MUST. |
older newer | first last |