21.3.10 DO..LOOP. 4tH 4tH Forth compiler
Quảng cáo
Quảng cáo
CHAPTER 21. PORTING GUIDE
Step 2: Replace OF with OVER = IF DROP
: .WEEKDAY ( daynum --- )
1 OVER = IF DROP ." Sunday" ELSE
2 OVER = IF DROP ." Monday" ELSE
3 OVER = IF DROP ." Tuesday" ELSE
4 OVER = IF DROP ." Wednesday" ELSE
5 OVER = IF DROP ." Thursday" ELSE
6 OVER = IF DROP ." Friday" ELSE
7 OVER = IF DROP ." Saturday" ELSE
ENDCASE ;
388
Step 3: Replace ENDCASE with DROP
: .WEEKDAY ( daynum --- )
1 OVER = IF DROP ." Sunday" ELSE
2 OVER = IF DROP ." Monday" ELSE
3 OVER = IF DROP ." Tuesday" ELSE
4 OVER = IF DROP ." Wednesday" ELSE
5 OVER = IF DROP ." Thursday" ELSE
6 OVER = IF DROP ." Friday" ELSE
7 OVER = IF DROP ." Saturday" ELSE
DROP ;
Step 4: Add as many THENs as there are ELSEs
: .WEEKDAY ( daynum --- )
1 OVER = IF DROP ." Sunday" ELSE
2 OVER = IF DROP ." Monday" ELSE
3 OVER = IF DROP ." Tuesday" ELSE
4 OVER = IF DROP ." Wednesday" ELSE
5 OVER = IF DROP ." Thursday" ELSE
6 OVER = IF DROP ." Friday" ELSE
7 OVER = IF DROP ." Saturday" ELSE
DROP THEN THEN THEN THEN THEN THEN THEN ;
Done! Ugly? Yes, but that’s what a CASE..OF..ENDOF..ENDCASE construct internally looks like! Wrapping it in some slick keywords doesn’t change that. If 4tH issues an error message saying that you’re nesting too deep, you’ll understand why. If you happen to convert a program that contains many CASE..OF..ENDOF..ENDCASE constructs it may be a bit tiresome, but remember you only have to do it once. If you don’t feel like editing your source manually, you can use the script case24th.th to do the job for you. Finally,
CASE..OF..ENDOF..ENDCASE is supported by the preprocessor
3 as well.
21.3.10
DO..LOOP
It is well-known in the Forth community that DO..LOOP is flawed. There have been several attempts to correct this, but they never got it right. On many occasions it even got worse.
But why is DO..LOOP flawed?
’DO’ puts the limit and the index on the Return Stack, but it doesn’t decide whether the loop is actually entered. So, every loop is executed at least once. After each iteration
’LOOP’ decides whether it iterates once more.
In our opinion it would have been better when ’DO’ had made that decision (like any other language), but we can still live with that. The real trouble came with DO..+LOOP.
3
See section 21.5.
CHAPTER 21. PORTING GUIDE 389
’+LOOP’ is a logical extension. Every single language allows you to change the step.
But contrary to what one might expect, ’+LOOP’ doesn’t terminate when the loop limit is reached or exceeded, but when the loop index crosses "the boundary between the loop limit minus one and the loop limit".
What does that mean? Well, consider these three loops and try to predict what will be printed. Note: every loop is executed at least once:
( 1) 5 0 do i . 1 +loop cr
( 2) 5 0 do i . -1 +loop cr
( 3) -5 0 do i . -1 +loop cr
You would probably expect to see:
0 1 2 3 4
0
0 -1 -2 -3 -4
And that is what you get when you use 4tH. But this is not what you will get with ANS-
Forth:
0 1 2 3 4
0 -1 -2 -3 .. 6 5
0 -1 -2 -3 -4 -5
The behaviour of the second loop is caused, because ’+LOOP’ doesn’t take into account that it is counting down. So it iterates until the loop index reaches the loop limit by wraparound arithmetic.
The behaviour of the third loop is caused by the ANS-Forth definition: the loop index must
"cross the boundary between the loop limit minus one and the loop limit". In this case, the boundary is between -5 and -6.
DO..+LOOP didn’t behave like this since the beginning of Forth: it was introduced in
Forth-83. We preserved the Forth-79 definition as closely as possible, because it is much more intuitive.
Some claim that ?DO..+LOOP will save it. As a matter of fact, it does. But only when the loop index and the loop limit are the same:
0 0 ?do i . loop
In that case the loop won’t be entered. But it still won’t save us for loops like this:
5 0 do i . -1 +loop cr
The authors of gForth claim that a whole host of new DO..LOOP words are the solution.
We don’t think so:
100 -100 do i . i 1+ 2/ negate +loop cr
The bottomline is: you can’t let two words make the same decision. 4tHs ’+LOOP’ checks which direction it is going (up or down) and evaluates the loop arguments accordingly. We feel it is the best we can do for you.
Is there no way we can circumvent these problems? Yes, there is. It may not be too elegant or even fast, but it solves the problem. We just emulate C’s for():
CHAPTER 21. PORTING GUIDE 390
12 >r begin r@ 10 < while r@ .
r> 2+ >r repeat r> drop cr
Which is "equivalent" to:
10 12 ?do i . 2 +loop cr
\ set up loop index
\ check loop limit
\ access loop index
\ increment loop index
\ next iteration
\ drop loop index
Except that it works as expected. And as an extra bonus it is portable to ANS-Forth. Are these differences between the ANS-Forth and 4tH implementation of DO..LOOP really that important? Not in practice. Nobody really wants a loop that depends on wrap-around arithmetic, and you’ll hardly ever see a ’+LOOP’ with a negative subscript. Everybody wants their programs to be understandable and maintainable, so the DO..LOOPs you’ll encounter will usually be well-behaved.
21.3.11
I/O
It is trivial to define the ANS-Forth FILE wordset in 4tH, but almost impossible to do the opposite. So if you want to make a portable program use the ANS-Forth FILE wordset by including the ansfile.4th library file. The reason why 4tH uses a different I/O subsystem is twofold:
1. 4tH’s I/O subsystem is far more powerful and elegant. Instead of defining a whole new wordset, 4tH reuses most of the available I/O words, like ’TYPE’, ’EMIT’,
’ACCEPT’ and ’REFILL’
4
, which is very Forth-like.
2. 4tH’s I/O was initially quite primitive and this was the only way to extend the system without breaking too much code.
This example is taken from gForth, but runs identically on both 4tH and gForth:
[defined] 4th# [if] include lib/ansfile.4th
include lib/compare.4th
[then]
[undefined] 4th# [if] s" lib/easy.4th" included
[then]
\ if this is 4tH, include
\ ANS Forth FILE wordset
\ and the word COMPARE
\ if this is not 4tH,
\ include 4tH compatibility
0 Value fd-in
0 Value fd-out
\ input file handle
\ output file handle
: open-input ( addr u -- ) r/o open-file throw to fd-in ;
: open-output ( addr u -- ) w/o create-file throw to fd-out ; s" foo.in" open-input s" foo.out" open-output
\ open input file
\ open output file
: show 2dup type cr ; \ show the line
256 Constant max-line \ size of basic buffer
4
As a matter of fact, a new set of words have been proposed that allow redirection of these words. 4tH already has that functionality.
Tải xuống
Quảng cáo