• Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r4wp

[Rebol School] REBOL School

Ladislav
3-Oct-2012
[1075]
Marc, your "private variables" like 'with, _recur_ can be made more 
private not needing to use the PROtECT function, in fact.
Gregg
3-Oct-2012
[1076]
Ah, no, I think I get it now.
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?