Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - tomxp411

Pages: 1 [2]
16
Programs / Re: Quick Directory Files Listing for Windows Only
« on: February 01, 2022, 11:36:56 pm »
I've always kind of wondered:  Why does no one ever mention the FILES command?

Because it's kind of useless if you need an array with a list of filenames, say for a file picker.

17
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 09:32:11 pm »
FOR isn't a command which puts anything on the stack.  Basically, a FOR... NEXT loop translates into something like the following:


FOR I = 1 TO 10
   PRINT I
NEXT

Translates to: (a simplified version here, for illustration)

I = 1
I_Start:
PRINT I
I =I + 1
IF I <= 10 THEN GOTO I_Start

At the core level, a FOR loop is nothing but a self-contained GOTO loop.  Using GOTO to exit it won't harm your program at all, nor affect any stack space.

Stack space mainly comes into effect when dealing with subs/functions and gosubs.

Yes, I was thinking more of classic BASIC implementations, where GOTO out of a loop leaves the wrong return address. MS BASIC does use a stack to store FOR addresses, and using GOTO out of a loop then causes the next NEXT statement to jump back to the wrong FOR statement.

Interestingly, try something like this on a Commodore 64:
10 FOR X=1 TO 100
20 GOTO 50
30 NEXT
50 GOTO 10

It does not actually cause a stack overflow, but if you wrap that inside of another FOR loop, the subsequent NEXT will advance the wrong loop counter.

QB64 does do this the way you'd expect (NEXT statements jump back to the correct FOR statement), but I'd hate to see someone learn to do this in QB64, work in another language or on another system, and then find their code breaks in unexpected ways because they relied on this behavior.

I figure if this is a learning experience for the OP, we might as well teach them the best way that will serve them well down the road. =)

18
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 08:52:24 pm »
Honestly, I don't see anything wrong with what you've got here.  (Though I imagine that GOTO would probably be in an IF condition or such instead of just stand-alone.)

It causes memory leaks, specifically in the stack. Since the NEXT doesn't get executed, the FOR's spot on the stack doesn't get removed, causing the stack to be pointing at the wrong place after exiting the loop. In extreme cases, this could cause errors further down the program, such as RETURN going to the wrong place or just running out of stack space if that loop gets repeated often.

Also, if this loop is contained inside a larger loop, this could cause the NEXT to advance the wrong loop. Consider this example

For I = 1 To 10
    For J = 1 To 10
        GoTo AfterJ
    Next
    AfterJ:
Next
Print I, J

On some BASICs, this actually prints 1,10. This is because the second NEXT actually advances J, rather than I. This is because BASIC doesn't know or care which FOR is being advanced. It just iterates the first loop in the stack and goes back to that line.

QB64 actually does this correctly (and presumably unwinds the stack on the GOTO out), but it's still unwise policy.


If you do need to exit a loop early, you should use the EXIT FOR command, which should properly unwind the stack.

Code: QB64: [Select]
  1. FOR I=1 TO 10
  2.   IF exit_condition THEN EXIT FOR
  3.   ...
  4.  

Also, in some languages, the loop counter (I) also goes out of scope after the loop completes, so it's never wise to count on a loop control variable's state after a loop. In fact, some compilers (I think c# does this) will actually throw an error when attempting to reference a control variable after a loop has completed.

If you do need to preserve the counter when you reach an early exit state, you can save it off to another variable. But there's another important consideration: FOR  actually the wrong construct to use when you may need to bail early. If you're doing something like searching a list or checking a range of values, that's actually what a WHILE loop is for.

Take this example of a linear search:


FOR I = 1 TO 100
  IF Domain(I) = Match THEN EXIT FOR
NEXT

This is actually better done as a WHILE loop, since WHILE signals that we may be exiting early:

I = 1
WHILE Domain(i) <> Match AND I <= UBOUND(Domain)
  I = I + 1
WEND

Either of these examples will work, but the latter example is more correct. This is because the thing we are looking for (Match) is canonically part of the loop condition, rather than being a line inside the loop somewhere.

19
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 08:37:15 pm »
Wow. Now that's a really good classic BASIC example. I can almost see the line numbers on it. =)

Now we can remove the GOTOs and simplify it even more:

Code: QB64: [Select]
  1. get_values:
  2.    input "Event Number: "; Event
  3.    input "Temperature: "; Temperature
  4.    input "SnowDays: "; SnowDays
  5.    input "Windspeed: "; Windspeed
  6. '
  7. on Event gosub Event1, Event2, Event3, Event4
  8. goto get_values
  9. '
  10. Event1:
  11.    Count1 = Temperature
  12.    if Count1 < 0 then goto Event2
  13.    print "RainEvent = RainEvent + 1"
  14.    return
  15. Event2:
  16.    Count2 = SnowDays
  17.    if Count2 > 31 then goto Event3
  18.    print "SnowEvent = SnowEvent + 1"
  19.    return
  20. Event3:
  21.    Count3 = Windspeed
  22.    if Count3 > 100 then goto Event4
  23.    print "WindEvent = WindEvent + 1"
  24.    return
  25. Event4:
  26.    print "HurricaneEvent = HurricaneEvent + 1"
  27.    return
  28.  

ON GOTO/ON GOSUB is one of my favorite tools in the MS BASIC toolbox, and it can be a good alternative to SELECT CASE for line-number BASIC when your domain is limited to a small, contiguous range of integer values. (Menus, for example, are a perfect place to use this.)

20
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 08:15:00 pm »
Hi @tomxp411,

please read the wiki page for Select Case and note well we have a EVERYCASE keyword here in qb64, which was actually used in @SMcNeill 's example.

But on the other hand I do also agree with you, that IFs without ELSE branch do it best here, what is actually what SELECT EVERYCASE does.

His last message was an IF/ELSE block, which would have failed in exactly the way I described.

EVERYCASE could be made to work, but it's also going to be more complex than what I posted, for the reasons I already gave: he really needs to settle all of his conditions before entering his CASE statement.


21
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 08:09:40 pm »
Here, it's a simple one directional downflow of code, with expressive labels to indicate what's going on in the code.  I don't really consider this "spaghetti code" as it doesn't weave up and down and get in a tangled mess, and I'd honestly have no issues at all in using GOTO in such a manner in my code.  GOTO is a tool in our toolbox for a reason, and sometimes, it just makes sense.  ;)

Yeah, GOTO is usually okay if you're branching forward and not out of control blocks.

For example, you should never do this:

Code: QB64: [Select]
  1. FOR I = 1 TO 10  
  2.    GOTO Done
  3. Done:

(I have seen BASIC programs that do this, especially early stuff from the 70s.)

Having said that... I prefer to avoid it unless there's not another clear way to branch where I need to go. The example above should work in BASIC, but it will not work in many other languages, where GOTO is either not present or jumping around in a CASE block is not allowed.

22
QB64 Discussion / Re: Select Case
« on: January 22, 2022, 06:59:12 pm »
Looking at the example given, this is not what SELECT CASE is for.

CASE statements are always meant to be mutually exclusive, and you can only pick one. Flow must always go from SELECT to a single CASE to END SELECT. Flow is not allowed to move between cases, or you get "spaghetti code."

So looking at your example... You're doing to much in one CASE block. You're still trying to figure out what your event is, and you need to identify that first, then act on it.

So you need some IF statements first, to properly parse your event and your outside parameters (Temperature, Snow Days, and Wind Speed) and then use the answer to make the correct calculation.

Code: QB64: [Select]
  1. Event1 = Event
  2. IF Event1 = 1 and Temperature < 0 THEN Event1 = 2
  3. IF Event1 = 2 and SnowDays > 31 THEN Event1 = 3
  4. IF Event1 = 3 and WindSpeed > 100 THEN Event1 = 4
  5.  
  6. SELECT CASE Event1
  7.   CASE 1
  8.     RainEvent = RainEvent+1
  9.   CASE 2
  10.     SnowEvent = SnowEvent + 1
  11.   CASE 3
  12.     WindEvent = WindEvent + 1
  13.   CASE 4
  14.     HurricaneEvent=HurricaneEvent +1

Note that I purposely did NOT use ELSE on the series of IF statements, because it's possible for event 1 to fall through to 2, 3, and 4. Event 2 can fall through to 3 and 4, and 3 can fall through to 4. So an event that starts as 1 must be tested up to 3 times. Using ELSE IF would skip the later tests.

@SMcNeill this is why your example doesn't quite work: Take a rain event where it's -10 degrees and the wind is blowing at 120. In this case, the correct case is "Hurricane". However, your code will fall through without incrementing any of the counters.


23
QB64 Discussion / Re: Trying to load an Atari disk image into memory
« on: January 22, 2022, 03:10:19 am »
Here is a complete example that loads the 92K image, adds the header, and writes back the ATR file with the header.

Code: QB64: [Select]
  1. DefLng A-Z
  2. Const IMG_LEN = 92176
  3.  
  4. ''Create test data
  5. 'Dim b As _Byte
  6. 'b = 0
  7. 'Open "KWEST01B.XFD" For Binary As #1
  8. 'For i = 1 To IMG_LEN
  9. '    Put #1, , b
  10. '    b = b + 1
  11. 'Next
  12. 'Close 1
  13. 'Print "File saved"
  14. 'End
  15.  
  16. Dim DiskData As String * IMG_LEN
  17.  
  18. Open "KWEST01B.XFD" For Binary As #1
  19. Get #1, , DiskData 'get the whole array
  20.  
  21. Dim Header As String * 32
  22. Header = "96028016800000000000000000000000"
  23.  
  24. Open "KWEST01B.ATR" For Binary As #1
  25. Put #1, , Header
  26. Put #1, , DiskData
  27.  

24
QB64 Discussion / Re: Trying to load an Atari disk image into memory
« on: January 22, 2022, 02:50:46 am »
Ok, let's start with: you don't need TEMP$ or the array. You definitely do not want an array of string*92126 objects. That gives you 92126*92126 characters, which is over 8GB.

You should be able to load data straight into a string*, like this:

Code: QB64: [Select]
  1. Dim DiskData As String * 92176
  2.  
  3. Open "KWEST01B.XFD" For Binary As #1
  4. Get #1, , DiskData 'get the whole array
  5.  
  6. dim Header as string * 32
  7. Header = "96028016800000000000000000000000"
  8.  
  9. Open "KWEST01B.ATR" For Binary As #1
  10. PUT #1, Header
  11. PUT #1, DiskData
  12.  
  13.  

25
Does anyone own a trademark on QB64? If you do, then you can get it shut down that way.

I was part of a ham radio club a few years ago who had their callsign's .com stolen by a former member, and that was the method they used to get it back.

26
QB64 Discussion / Re: Wiki accounts: apply here.
« on: January 22, 2022, 02:39:10 am »
I'd like to apply. I'd like to think I'm pretty good at writing example code, documenting software, and generally being helpful.

I've been programming BASIC since the 80s... BASIC 2, Quick BASIC, Visual BASIC, and now QB64, and I also work for a technology company you'd have heard of (I don't talk about it in public forums, but feel free to PM me to ask for details.)

27
QB64 Discussion / Re: associativity of the exponential operator
« on: January 22, 2022, 02:35:34 am »
The idiot's working rule (I always follow this):

Always put brackets in.

This is true. My entry exam at a software company I worked for actually had an order of operations problem on the test. It was something like
when IF A AND B OR C AND D gives unexpected results, what might be the cause? And the obvious answer is "the order of operations."

In the case of operators that are equal in rank, you're supposed to evaluate from left to right. So A^B^C should always evaluate A^B first, then n^C.

But after seeing the number of people who get confused by 5 + 6*0 - 3 on Facebook, I think even software engineers don't really understand the math they're implementing sometimes.

28
No, there was no need for assembly. Coders just had to call an interrupt, and the parameter going in determined whether you'd get back the X or Y position of the pointer, or the button state.

http://www.petesqbsite.com/sections/tutorials/zines/basix/3-mouse.txt


Pages: 1 [2]