LB Booster
General >> General Board >> about GOSUBs
http://lbb.conforums.com/index.cgi?board=general&action=display&num=1465327134

about GOSUBs
Post by tsh73 on Jun 7th, 2016, 7:18pm

They are came up as a side-topic in reply 55 at this thread
Card graphics not sticking
And I thought matter is rested.
But alas today I saw this on LB forum:
Quote:
Kind regards and may all your Gosubs Return

so. If anything this is a "redeeming feature" for me.

May the Source be with you! wink))
Re: about GOSUBs
Post by Richard Russell on Jun 7th, 2016, 9:36pm

on Jun 7th, 2016, 7:18pm, tsh73 wrote:
But alas today I saw this on LB forum: "Kind regards and may all your Gosubs Return"

I can't see that comment (probably in a part of the forum I'm not allowed to read) but if it was a signature I don't think it is necessarily recommending the use of GOSUBs, but rather that if you do use them you'd better hope that they RETURN eventually!

Actually there's a serious point here. If, for example due to a convoluted program, the GOSUB never RETURNs (maybe it ends up at a WAIT) the result will be a memory leak that eventually, if the program is left running for long enough, will cause all the memory to be exhausted and a catastrophic failure.

If you use CALL rather than GOSUB it's less likely that a problem of this sort would go unnoticed, if only because you have to think more carefully about program structure.

Richard.

Re: about GOSUBs
Post by Mystic on Jun 7th, 2016, 10:05pm

WOW! After reading that I feel like an idiot.

My programs are all about the GOSUBs. Throwback from TRS80, C64, Amiga QBASIC days I guess. LOL

Time to research more on CALLS and get my programming shit together I guess. sad
Re: about GOSUBs
Post by Richard Russell on Jun 8th, 2016, 08:22am

Here's an example of the sort of thing that can happen by accident. Try running the program in the LBB debugger and watch the 'Stack usage' figure:

Code:
    timer 10, [tick]
    wait
    
[tick]
    gosub [handler]
    wait
    
[handler]
    ' do something
    wait 

Of course with such a small program the problem is obvious, but in a large or convoluted program it could easily go unnoticed.

If I run that same program in the LB 4.04 debugger it doesn't give me any indication that anything is wrong.

Richard.
Re: about GOSUBs
Post by Mystic on Jun 9th, 2016, 3:55pm

I've now removed all GOSUBS from one of my main programs, and replaced them with SUB's or FUNCTION's as needed.

I have two more larger ones to tackle. From now on I will tread lightly around the GOSUB and use these alternatives.

Apparently an old dog CAN learn new tricks! smiley

Thanks Richard!
Re: about GOSUBs
Post by RobM on Jun 9th, 2016, 8:02pm

Just ran the following program on my cabinet program. I have 9,198 gosub commands. :o

I seem to remember a discussion about the speed of gosubs, subs and functions back when I started with LB. Carl said gosubs were faster in general because subs & functions need to allocate memory each time they were used, whereas gosubs do not. Wonder if the same is true in LBB?

Code:
    filedialog "Open text file", "*.bas", fileName$
    open fileName$ for input as #1
    while not(eof(#1))
      line input #1, temp$
        if instr(temp$,"gosub")>0 then count=count+1
      wend
    print count
    close #1
    end
 

Re: about GOSUBs
Post by Richard Russell on Jun 9th, 2016, 9:41pm

on Jun 9th, 2016, 8:02pm, RobM wrote:
I seem to remember a discussion about the speed of gosubs, subs and functions back when I started with LB. Carl said gosubs were faster in general because subs & functions need to allocate memory each time they were used, whereas gosubs do not. Wonder if the same is true in LBB?

It's not difficult to do the test. Here is the program I used:

Code:
    t0 = time$("ms")
    for i = 1 to 200000
    next
    t1 = time$("ms")
    for i = 1 to 200000
      gosub [MySub]
    next
    t2 = time$("ms")
    for i = 1 to 200000
      call MySub
    next
    t3 = time$("ms")
    print "Total time taken for GOSUBs = "; (t2 - t1) - (t1 - t0) ; " ms"
    print "Total time taken for CALLs =  "; (t3 - t2) - (t1 - t0) ; " ms"
    end
    
[MySub]
    return
         
sub MySub
end sub 

Results on my PC:

LBB v3.05:
Total time taken for GOSUBs = 114 ms
Total time taken for CALLs = 56 ms


LB v4.04:
Total time taken for GOSUBs = 1328 ms
Total time taken for CALLs = 4232 ms


So in LBB CALL is roughly twice as fast as GOSUB, and LBB's CALL is about seventy-five times faster than LB's CALL!

But of course the benefit will be much reduced once the subroutines actually contain some useful code. Also, it's arguably unfair not to pass any parameters to the SUB; passing just a single parameter makes CALL slower than GOSUB.

In most practical cases I would expect the time taken by the code within the subroutine to dominate the time taken in the actual GOSUB or CALL, so speed should not be an issue in deciding which to use.

Richard.

Re: about GOSUBs
Post by RobM on Jun 9th, 2016, 9:55pm

Similar results here.

LBB 3.03:
Total time taken for GOSUBs = 35 ms
Total time taken for CALLs = 13 ms


LB 4.04:
Total time taken for GOSUBs = 546 ms
Total time taken for CALLs = 1965 ms
Re: about GOSUBs
Post by Jack Kelly on Aug 2nd, 2016, 09:46am

I've been looking at the code for Freeform 4. It contains about 68 gosub subroutines -- ones starting with a label and ending with a RETURN. 233 GOSUB calls are made to these subroutines. To convert all the gosubs to calls in this program would involve declaring possibly hundreds of global variables. Are there any downsides to global variables? Could this be a redeeming factor in using a GOSUB?

Re: about GOSUBs
Post by Richard Russell on Aug 2nd, 2016, 1:05pm

on Aug 2nd, 2016, 09:46am, Jack Kelly wrote:
Are there any downsides to global variables? Could this be a redeeming factor in using a GOSUB?

The important question is: "what are those global variables used for?". With a traditional GOSUB..RETURN, global variables are the only way to pass values into a subroutine (inputs) or return values back from a subroutine (outputs). When replacing the GOSUB with a CALL any global variables used solely for those purposes should be substituted by the appropriate 'modern' equivalents: parameters for inputs and BYREF parameters (or the value returned from a FUNCTION) for outputs.

Having substituted the global variables used for inputs and outputs, any other global variables still accessed from within the subroutine will, as you say, have to be declared as GLOBAL. If the program is well structured, there should be relatively few such variables and they should be 'inherently' global in scope (i.e. making them GLOBAL should actually enhance the readability of the code rather than the opposite).

However if there are global variables accessed within the subroutine that are neither 'inputs', 'outputs' nor of genuinely 'global scope' then the program is poorly structured. You cannot convert a poorly structured program into a structured program just by replacing GOSUBs with CALLs, so in that case you should probably give up the struggle and declare the program to be beyond redemption!

Richard.
Re: about GOSUBs
Post by joker on Aug 2nd, 2016, 1:46pm

Oh Richard, your succinct replies are beyond reproach! cheesy
Re: about GOSUBs
Post by BrianM on Aug 2nd, 2016, 4:10pm

Gosubs are not completely redundant as they can be used inside subroutines and functions. The advantage in using a gosub over using a subroutine or function is that the gosub code "sees" the same variables as the calling sub or function and so no extra global variables are required to share data. The disadvantage is that you cannot use parameters. In certain situations this can result in a simpler code structure.

This is similar to internal and nested subroutines available in other languages.

I have found that in LBB that the labels for the gosub code must be unique throughout the program and not just in the enclosing subroutine or function. This is not the case in Liberty Basic.

Brian Matthews
Re: about GOSUBs
Post by Richard Russell on Aug 2nd, 2016, 4:51pm

on Aug 2nd, 2016, 4:10pm, BrianM wrote:
I have found that in LBB that the labels for the gosub code must be unique throughout the program and not just in the enclosing subroutine or function. This is not the case in Liberty Basic.

That is not correct, in general, as the simple test program below demonstrates:

Code:
    gosub [label1]
    call mysub
    call anothersub
    gosub [label1]
    stop
    
[label1]
    print "At [label1] in the main program"
    return

sub mysub
    gosub [label1]
    exit sub
[label1]
    print "At [label1] in sub mysub"
    return
end sub    

sub anothersub
    gosub [label1]
    exit sub
[label1]
    print "At [label1] in sub anothersub"
    return
end sub 

You can probably contrive a situation in which it won't work, for example by placing the destination for the outer GOSUB after rather than before the SUBs, but if you follow the recommendation to place all SUBs and FUNCTIONs at the end of the program that situation won't arise.

Richard.

Re: about GOSUBs
Post by BrianM on Aug 2nd, 2016, 5:07pm

This is my test program that works in LB but not LBB. I can't see an error.
Code:
call mysub1
call mysub2
end

sub mysub1
for i = 1 to 5
    gosub [internal_sub]
next

exit sub

[internal_sub]
    print "internal sub(mysub1)",i
    return

end sub

sub mysub2
for i = 1 to 5
    gosub [internal_sub]
next

exit sub

[internal_sub]
    print "internal sub(mysub2)",i
    return

end sub
 


Brian Matthews
Re: about GOSUBs
Post by Richard Russell on Aug 2nd, 2016, 5:35pm

on Aug 2nd, 2016, 5:07pm, BrianM wrote:
This is my test program that works in LB but not LBB. I can't see an error.

That's a quite separate issue, caused by you including an 'illegal' character (an underscore) in your labels. This is what the LB Help File has to say about label names: "The label can be either a traditional line number or a branch label in the format [branchLabel] where the branch label name can be any upper/lowercase letter combination. Spaces and digits are not allowed".

It's not as precise as I would like, but I interpret "any upper/lowercase letter combination" as not permitting the use of an underscore - after all underscores definitely aren't allowed in variable names (except Windows Constants) and it's uncommon for different rules to apply.

If you remove the underscores from your label names (or substitute a character which is legal in a variable name like a dot) the program works as expected:

Code:
call mysub1
call mysub2
end

sub mysub1
for i = 1 to 5
    gosub [internalsub]
next

exit sub

[internalsub]
    print "internal sub(mysub1)",i
    return

end sub

sub mysub2
for i = 1 to 5
    gosub [internalsub]
next

exit sub

[internalsub]
    print "internal sub(mysub2)",i
    return

end sub 

Richard.

Re: about GOSUBs
Post by BrianM on Aug 2nd, 2016, 8:19pm

Richard

I often use underscores in other languages and so I used them in LB labels without thinking whether they were allowed or not and as they worked I never checked. So my mistake.

The LB documentation is often at odds with what is actually implemented. The help talks about alphanumeric labels (those in square brackets) and then goes on to say spaces and numbers are not allowed, in which case they are not alphanumeric. The basic code for freeform uses . and digits in labels.

A quick check has shown that the characters _.%&\?{}!# can also be used in labels and there could be others.

I think your approach of using the same syntax for labels and variables is probably the most sensible one.


But to get back to my original point. Despite many people declaring that gosubs are obsolete, they still have their uses.

Brian Matthews
Re: about GOSUBs
Post by Richard Russell on Aug 2nd, 2016, 8:37pm

on Aug 2nd, 2016, 8:19pm, BrianM wrote:
The LB documentation is often at odds with what is actually implemented.

You're telling me: having to discover largely by trial-and-error what LB does and doesn't do, rather than being able to rely on the documentation, is one of the main reasons why it took so long to get LBB to the degree of compatibility it currently has.

Quote:
Despite many people declaring that gosubs are obsolete, they still have their uses.

I strongly disagree. I haven't used a GOSUB in a program for around 35 years, and I've not missed them.

Richard.

Re: about GOSUBs
Post by BrianM on Aug 2nd, 2016, 9:21pm

Richard

I would hazard a guess that your Basic programming has largely been with BBC Basic which uses dynamic scoping for variables so that a called FN or PROC sees the same variables as the calling FN or PROC (or main) unless local variables or parameters are used. This makes data sharing between FNs and PROCs easy and without the need for special GLOBAL variables or passing large amounts of data via parameters. What I am suggesting is this can be emulated in LB and LBB which use static variable scoping by using GOSUBS inside SUBS and FUNCTIONS. In some cases this can reduce the complexity of the code structure. I do not advocate general use of GOSUBS

Brian Matthews

Re: about GOSUBs
Post by Richard Russell on Aug 2nd, 2016, 10:13pm

on Aug 2nd, 2016, 9:21pm, BrianM wrote:
I would hazard a guess that your Basic programming has largely been with BBC Basic which uses dynamic scoping for variables so that a called FN or PROC sees the same variables as the calling FN or PROC

Sadly yes. I work around that shortcoming in two ways: firstly those variables that I really want to be GLOBAL get commented as such at the start of the program, so that at least it's clear to the reader even though it's not enforced by the language. Secondly I use strict naming conventions (local variable names consisting of only lowercase letters whilst globals use mixed case); the Cross Reference utility will then warn me if I inadvertently access a variable which is neither local nor global.

Whilst neither of those strategies compensates for the weak scoping of the language they are better than nothing.

Quote:
This makes data sharing between FNs and PROCs easy and without the need for special GLOBAL variables or passing large amounts of data via parameters. What I am suggesting is this can be emulated in LB and LBB

True, but why would you want to emulate a weakness of a language? One can justify static (lexical) scoping rules that allow an inner block to see the variables declared in an outer block, but not dynamic scoping that allows the same thing to happen at run time even if the functions are physically separated in the code.

I would always explicitly pass as parameters variables that need to be accessed by a sub-function, even if the dynamic scoping allows it to see those same variables.

Richard.

Re: about GOSUBs
Post by BrianM on Aug 3rd, 2016, 12:53pm

Richard

I agree totally with your last post.

The dynamic scoping of BBC Basic can cause problems by forgetting to declare a variable as local and inadvertently updating the variable with the same name in the calling PROC or FN. I presume this is the weakness you are referring to.

I am not trying to emulate dynamic scoping but rather the file scoping of the C programming language. LB and LBB only provide global or local variables. What I would like is something in between.

If a large sub or function is refactored into a number of smaller subs and functions then the problem of data sharing arises. As you say this can be achieved by the use of globals (suitably named) or by passing parameters. I prefer the passing of parameters but this does not look right when the same parameter list is used over and over again and with parameters passed BYREF. This cries out for C file scoping or perhaps using OOP classes and class properties. In these instances I consider using gosubs but I realise I am not going to pursuade you that it might be a good idea. To my mind gosubs within subs and functions do give :-
Quote:
scoping rules that allow an inner block to see the variables declared in an outer block


Perhaps I should forget LB compatibility and investigate the OOP extensions of LBB.

Brian Matthews
Re: about GOSUBs
Post by Richard Russell on Aug 3rd, 2016, 10:04pm

on Aug 3rd, 2016, 12:53pm, BrianM wrote:
I prefer the passing of parameters but this does not look right when the same parameter list is used over and over again and with parameters passed BYREF.

The technique I usually adopt in BBC BASIC is to store the 'shared variables' in a structure. That structure can be made LOCAL (or PRIVATE) to the enclosing function, and can be conveniently passed - as a single parameter - to all those sub-functions that need to share its contents. This seems to be the best of both worlds: no need for the sub-functions to implicitly share variables through lexical scoping, but equally no need to pass multiple parameters by reference each time.

Of course this solution doesn't help in Liberty BASIC, because structures are always global. This is mindbogglingly stupid (almost as stupid as arrays being always global) - are you listening Mr Gundel? tongue

Quote:
Perhaps I should forget LB compatibility and investigate the OOP extensions of LBB.

As has been discussed here before, it is reasonable to utilise the OOP features of LBB not because you actually want to write 'Object Oriented' code as such, but simply as a workaround for the absence of local arrays and structures.

Richard.