Friday, August 16, 2013

Petit Computer Journal #9



Loops and Banner


A quick diversion here, I want to discuss a little bit about loops. We have been doing it, and it's not hard to do, but that doesn't mean people understand them completely. I keep seeing people do things in inefficient ways. I know I'm bucking the convention here, but I think making the program work the way you want it is the minimum requirement. You should also improve the program in such a way that it is well structured, implemented, and clear.

So, what's so hard about loops? Nothing. That's why it's important for you to master loops. There are several kinds:

1. Infinite loop: This is easy. You basically want to repeat something over and over. Most C programmers would use this construct:

while (1) { ... }

That's an infinite loop. But I think this is the case where GOTO is better. Here it is in Smile BASIC:

@LOOP
...
GOTO @LOOP

And that's it for infinite loop. It's good for use as IDLE loop.

2. REPEAT-UNTIL loop: Also known as DO-WHILE, this is just like infinite loop, but it ends when there is a certain condition to be met. You can do it like this:

@LOOP
...
IF (NUM<MAXNUM) GOTO @LOOP

or this

@LOOP
...
IF !(NUM>=MAXNUM) GOTO @LOOP

or this

FOR I=1 TO 1:VSYNC 1:I=BTRIG():NEXT

That last example is basically "Press any BUTTON to continue". It works because as long as I is zero, the loop repeats.

Another example of REPEAT-LOOP would be this program in finding out the value of PI using fraction:

'PI
N1=3:N2=1
R1=ABS(PI()-(N1/N2))

FOR I=1 TO 1
R2=(ABS(PI()-(N1/N2)))
IF (N1/N2)<PI() THEN N1=N1+1
IF (N1/N2)>PI() THEN N2=N2+1
I=!R2:'TERMINATE WHEN I==0.

IF R2<R1 THEN R2=R1:'NOT SURE ABOUT EQUATION HERE
NEXT

?PI()
?(N1/N2)

The structure is similar to HI-LO game, and it's a good exercise for you to do in order to see if you understand the concept. In the meantime, if you see the statement I=!R2 where R2 is fraction, we can say that I'm abusing ! operator to the max!

Haha, joking aside, I do tend to push the interpreter beyond standard convention. It takes a lot of trial and error, with mostly error on the result. But you can't argue that the result is good.

3. WHILE-WEND loop: This loop differs from REPEAT-UNTIL loop by the fact that the code may not be executed at least once. By putting the condition on top of the loop, the whole code may be skipped.

@LOOP
IF (NUM>=MAXNUM) GOTO ENDLOOP
...
GOTO @LOOP
@ENDLOOP

or this

@LOOP
GOTO @ENDLOOP
...
@ENDLOOP
IF (NUM<MAXNUM) GOTO @LOOP

or this MADLIBS example

A$="HELLO [N]. HOWDY [N]. GOODBYE [N]."
LINPUT "NAME";N$
T$="[N]"

@WHILE LOOP
FOR I=0 TO INSTR(A$,T$)
A$=SUBST$(A$,INSTR(A$,T$),LEN(T$),N$)
NEXT
?A$

Yes, it's that easy! See explanation on FOR-LOOP on how this works.

4. FOR-LOOP. The standard BASIC loop. The workhorse loop of BASIC. How difficult can it be? It's just like WHILE loop, except the value of I is incremented within the code. Smile BASIC will not execute the loop when the parameters do not match, such as this:

FOR I=0 TO -1 STEP 1

I took advantage of this fact by putting INSTR() directly on the loop declaration. You don't see "STEP 1" written out, but it is implied. If you need stepping other than 1, then you need to write it out. STEP 4, STEP -1, and so on.

NUM=0
@LOOP
IF (NUM>MAXNUM) GOTO ENDLOOP
...
NUM=NUM+1:'OR NUM=NUM+N WHERE N IS THE STEP VALUE
GOTO @LOOP
@ENDLOOP

FOR NUM=0 TO MAXNUM: ... :NEXT

And that's it for LOOP structures. There is a couple more things we need to talk about. BREAK and CONTINUE.

CONTINUE is the way for the loop to skip processing to the next step. That is, whatever we want to do, we're done! We don't want to do any more.

@LOOP
...
IF CONTINUE GOTO LOOP
...
GOTO @LOOP

Let's do an example with FOR loop. Let's say we want to skip displaying vowels. We can do this:

A$="HELLO SAILOR!"
FOR I=0 TO LEN(A$)-1
IF INSTR("AEIOU",MID$(A$,I,1))>=0 THEN NEXT
? MID$(A$,I,1);
NEXT
?:?"DONE!"

Yes, you have two NEXT statements in there. The NEXT on IF line functions as CONTINUE. Pretty easy, right? (1)

BREAK, however, is not so easy. Not that it's difficult to understand, but it's difficult to implement because we cannot GOTO out of LOOP. Or maybe we can, but I don't quite understand how it works exactly despite my numerous experiments. So, we won't GOTO out of FOR loop.

@LOOP
...
IF BREAK GOTO @ENDLOOP
...
GOTO @LOOP
@ENDLOOP

That's it. Just breaking out of the loop cycle. Now let's see it with FOR loop.

A$="HELLO SAILOR!"
FOR I=0 TO LEN(A$)-1
IF INSTR(" ",MID$(A$,I,1))>=0 THEN I=LEN(A$):GOTO @ENDLOOP:'NEXT
? MID$(A$,I,1);
@ENDLOOP
NEXT
?:?"I=";I

You can see immediately that the structure isn't good. We have to use GOTO @ENDLOOP, instead of a simple NEXT because the program will return to the line of execution, resulting NEXT without FOR error. We have to put the label @ENDLOOP in separate line. The value of I is 14, which may not be what we want. In all, BREAKing out of loop isn't a good solution with this BASIC.

SAMPLE PROGRAM: TOUCH SCREEN KEYBOARD
@TOUCH
MX=256:MY=192:'MAXX,MAXY
SX=8:SY=6:'SPANX,SPANY
WX=FLOOR(MX/SX):WY=FLOOR(MY/SY):'WIDTHX,WIDTHY
TX=TCHX:TY=TCHY
FOR I=1 TO TCHST
IF TX
VSYNC 1:GFILL 0,0,MX,MY,0:GFILL KX*WX,KY*WY,(KX+1)*WX,(KY+1)*WY,8
LOCATE 0,0:?"KX=";KX;"  KY=";KY;"   "
NEXT
GOTO @TOUCH

Here is a simple way to divide the screen to rectangular areas.
MAXX,MAXY = SCREEN AREA/KEYBOARD SIZE
SPANX,SPANY=NUMBER OF KEYSPAN X,Y
WIDTHX,WIDTHY=INTERNAL VALUES USED FOR DRAWING
KX and KY =THE VALUE IN SPANX,SPANY THAT DENOTES ACTIVE CELL

If you look at FOR I=1 TO TCHST code, you maybe forgiven to think that it is a loop. It's not a loop. It is a multi-line IF, which is something Petit Computer does not support. It works in this case, but it's a special case.


SAMPLE PROGRAM: BANNER


Here is an example of nested loop. That is, loop within loop structure. It is also an example of GPUTCHR, where I'm using it for fine scrolling and zooming technique.

@ASKB
ACLS
LINPUT "MESSAGE 1/3?";B1$
LINPUT "MESSAGE 2/3?";B2$
LINPUT "MESSAGE 3/3?";B3$
B$="      "+B1$+B2$+B3$+"      "
ACLS

@LOOP
FOR I=1 TO LEN(B$)-5
FOR X=63 TO 0 STEP -4
VSYNC 1:IF BTRIG() THEN GOTO @ASKB
GCLS
FOR J=-1 TO 3:GPUTCHAR X+(J*64),40,"BGF0",ASC(MID$(B$,I+J,1)),0,8:NEXT
NEXT
NEXT
GOTO @LOOP

The sharp eyes among you will see that I did GOTO out of FOR loop with GOTO @ASKB. How does that work? Isn't GOTO out of loop is bad? Yes, it is. I don't understand it myself. It seems to work fine in this case, but I still discourage its use. Good for demos, but please don't use it for professional programs.

'HI-LO
S=RND(100)+1
FOR I=1 TO 1
INPUT "GUESS A NUMBER(1-99)";N
IF N>S THEN ?"TOO HIGH"
IF N<S THEN ?"TOO LOW"
I=!ABS(N-S)
NEXT
?"CORRECT!"

'HI-LO
S=RND(100)+1
@LOOP
INPUT "GUESS A NUMBER(1-99)";N
IF N>S THEN ?"TOO HIGH"
IF N<S THEN ?"TOO LOW"
IF N==S THEN ?"CORRECT!":END
GOTO @LOOP




(1) BTW, even though the CONTINUE looks fine, you have to be careful that you don't end the loop at CONTINUE, because the loop will end there and you will get NEXT without FOR error. There is the reason why I end the string with "!" when the condition only checks for vowels. That's being sneaky, and it's all right for you to do it, but do document it. Try adding exclamation mark at INSTR:

A$="HELLO SAILOR!"
FOR I=0 TO LEN(A$)-1
IF INSTR("AEIOU!",MID$(A$,I,1))>=0 THEN NEXT
? MID$(A$,I,1);
NEXT
?:?"DONE!"

No comments:

Post a Comment