tehologist / forthkit

BSD 2-Clause "Simplified" License
35 stars 11 forks source link

recurse doesn't work, here is my fix #1

Open Eszartek opened 7 years ago

Eszartek commented 7 years ago

Using the following factorial definition as a test:

: fac dup 1 = if drop 1 else dup 1- recurse  * then ;

"5 fac ." returns 20 as a result which is incorrect.

Redefining recurse (and fac again which depends on it) as :

: recurse last @ code> @ , ; immediate
: fac dup 1 = if drop 1 else dup 1- recurse  * then ;

"5 fac ." returns 120 as a result, which is correct.

tehologist commented 7 years ago

Merged into my master. Considering changing recurse to just be a branch back. : recurse compile branch last @ code> @ , ; immediate

Eszartek commented 7 years ago

Wouldn't compiling a branch there effectively be the same a compiling a NOP there? In the end, the code address of the current word being defined just needs to be executed , which comma accomplishes without the help/overhead of a branch call.

Do you see something else of value that the branch adds, perhaps readability? To me, the explicit branch does actually give a cue as to whats going on in that definition of recurse.

tehologist commented 7 years ago

The big reason for the branch is to optimize away the call return. Without the branch it will place the current position on the return stack before branching to the code and then when it exits would have to unwind every call it made. The branch simply jumps to the address and will exit from the original function on exit.

Eszartek commented 7 years ago

Ahh, great explanation. I was going to try to test to see how the return stack was being used in each scenario, as I still don't have a full mental map as to "whats happening when".

So basically, with the branch, you could recurse almost indefinitely without overflowing the return stack?

Edit: testing with :

: fac rpp @ . dup 1 = if drop 1 else dup 1- recurse  * then ;

"5 fac ." results with 184 182 180 178 176 120 so I see the return stack being consumed.

Now to figure out why the branch version isn't working. Perhaps it's short circuiting the "then" at the end of the factorial word. "5 fac ." results with 184 184 184 184 184 1 so it is recursing, just not hitting the multiplication.

tehologist commented 7 years ago

I see the problem now, the original RECURSE does work as designed.

OK. : fact RECURSE DUP 1 > IF DUP 1- fact * THEN ;
OK. 5 fact
OK. .S
 120  <SP

All RECURSE does is allow you to refer to the definition before the end of the definition, nomally function isn't usable until after ; this is so can use an earlier definition of a word within a new definition.

: fact DUP FOR AFT R@ DUP IF * THEN THEN NEXT ;

This is the non recursive way. It runs the loop x number of times duping the initial count. The AFT THEN will make the loop skip first iteration. R@ will count down to 0.

Eszartek commented 7 years ago

For the original design, it should be named "recursive" so that the behavior is understood. Then the recurse word could be included as well (just not ever needed together in the same definition). It appears that gforth has both words, so depending on your style you could use either:

https://www.complang.tuwien.ac.at/forth/gforth/Docs-html/Recursion-Tutorial.html

I haven't looked at gforth before, so I was unaware of the word "recursive". I like it!

tehologist commented 7 years ago

http://www.forth.org/eforth.html

This is the forth I was referencing. Originally my goal was to see what the minimal implementation would require to implement since it is based on a handful of primitives.

Eszartek commented 7 years ago

It's all clear now, It just took a few gyrations for it to come together for me.

tehologist commented 7 years ago

Excellent.