Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas

*************** SPECIAL EDITION *************************

The Liberty Basic Newsletter - Issue #67 - MAR 2000

© 2000, Cliff Bros and Alyce Watson

All Rights Reserved

In this issue:

In future issues:


LIBERTY BASIC 2.0 IN REVIEW

If you haven't already, get the Alpha 4 of Liberty BASIC v2.0 here:

http://world.std.com/~carlg/LBALPHA4.ZIP

Carl has been very sensitive to the needs and suggestions of Liberty BASIC programmers, and version 2.0 provides a whole sack-ful of goodies! User defined functions were on the top of many people's lists, and they are discussed later in this newsletter.

Here are some of the other new features:

 

 


PLEASE TEST AND GIVE FEEDBACK!

 

The more people testing, the better version 2 will be, so please download the new alpha and give it a try. What should you do to test? Try out the sample programs and see if you understand what is going on, then try to modify them. Write your own routines, making use of the new features.

Use the alpha version to run your old programs, to see if there is compatibility, and to check for new bugs that may have crept in.

What should you do with your test results? It is most helpful to report errors, bugs, and any problems you had, including difficulty you have in using and understanding the new features. Post your findings on the alpha-test message board here:

http://tigernetsoftware.hypermart.net/wwwboard/

A public posting is much better than a private message to Carl. Only one person needs to post a bug report this way, so Carl doesn't need to sift through 27 private messages that all report the same bug. A public posting also gets information to many people and allows more discussion.

If you do find a bug, try to be as specific as possible when describing it. For instance, saying "Graphics windows are not being filled with the correct color" is much more helpful than saying "I had a problem with graphic color." If possible, include a small snippet of code that reproduces the problem.


EDITOR'S NOTE

Perhaps the most exciting feature to be added to Liberty BASIC in version 2 is the ability to define our own functions. We have a real treat for this newsletter!

Carl Gundel has written an article to share with us his thoughts on user defined functions and their implementation in Liberty BASIC. He hopes that this article will generate discussion on this very important issue. This is a chance for all of us to have a voice in the development of Liberty BASIC. Thanks Carl!

For those who feel a bit lost when confronted with the concept of user defined functions, there is a beginner's tutorial at the end of this newsletter.

Thank you, Carl, for providing this wonderful language, and for your continued involvement with the community of people who have come together in friendship to share our loveof Liberty BASIC.


USER DEFINED FUNCTIONS IN LIBERTY BASIC 2.0

-- THEORY AND IMPLEMENTATION --

© CARL GUNDEL, MARCH 2000

User functions in Liberty BASIC v2.0

 

Probably the single most significant feature of Liberty BASIC v2.0 is its new support for user defined functions. This is an important feature because it improves substantially our ability to create modular programs and also makes it easier to make libraries of code that are reusable between programs.

We will also be more able to share code with each other.

 

This article isn't meant to be a tutorial on functions. Instead it describes the current state of the user function feature of Liberty BASIC v2.0 alpha 4. In addition, we will explore what is possible for the final release of Liberty BASIC v2.0, as opposed to what is currently implemented.

Local things - Variables and branch labels

Using GOSUB and RETURN, we Liberty BASIC programmers have been able to place often repeated code in our programs into subroutines. This is definitely useful, but when using this approach we have to worry about not using variable names for more than one thing. Subroutines don't let us choose names for our variables without first looking around at the entire program to make sure we don't reuse a variable whose contents shouldn't be tampered with.

User functions on the other hand let us choose whatever names we feel are most appropriate for variables for a given task.

We call these kind of variables local variables.

 

Here is a short code sample which is written as a subroutine, and then we'll follow it with an equivalent user function:

'Return a string without quotes
'This is used when getting typed input
[noQuotes]
    noQuotes$ = ""
    for nqLoop = 1 to len(nqText$)
        if mid$(nqText$, nqLoop, 1) <> chr$(34) then
            noQuotes$ = noQuotes$ + mid$(nqText$, nqLoop, 1)
        end if
    next nqLoop
    return

In this case I put the letters nq in front of all my variable names to help make sure there is no conflict between the variables in this subroutine and the variables in the rest of the program. These sort of conflicts often lead to nasty and hard to figure out bugs!

Now here's the function:

'Return a string without quotes
'This is used when getting typed input
function noQuotes$(text$)
    for x = 1 to len(text$)
        if mid$(text$, x, 1) <> chr$(34) then
            noQuotes$ = noQuotes$ + mid$(text$, x, 1)
        end if
    next x
end function

In the function we are able to choose very simple names for each variable. We can coincidentally have the same variable name in many different functions, but each identically named variable represents a value unique to the function it is defined in.

To return a value from a function, all we have to do is set the value of a variable having the same name. In the example function noQuote$() above we set the variable noQuote$ to be the string we want to return.

Each time we use a function (this is like using GOSUB to use a subroutine) it makes new copies of its variables, just as all variables have no value when any Liberty BASIC program has not started to run yet.

Because of this, it was possible to eliminate the line in the subroutine which resets the variable (noQuote$ = "") because variables in any function are created from scratch each time the function is called.

Understand that this process does not erase the previous values of the same variables in that function. Each use of a function has its own variables. When the function ends its variables are discarded. This is often unimportant, but sometimes it is useful to write a function which calls another function, and that second function may need to call the first (or it may call another which calls the first).

Just imagine what would happen if you went to the bank and started to fill out a form for a loan and had to excuse yourself to put money in the parking meter, and then another customer came in and began writing on your loan application!

The ability for a function to use itself to get the job done is called recursion.

Here is a very simple (and probably useless) example of the use of recursion to count to ten:

print countToTen(0)
 
function countToTen(value)
    countToTen = value
    if value < 10 then countToTen = countToTen(value+1)
end function

In addition to local variables, functions have local branch labels. This means that I do not need to worry if I am duplicating branch labels from one function to the next, or from the main application space (the part of the program that starts to run at the beginning of your program).

 

Global things

Arrays and things with handles are globally visible in Liberty BASIC v2.0. This has its pros and cons. For many kinds of programming it makes code simpler. We don't need to add any code instructing Liberty BASIC to pass arrays and things with handles (files, windows, and comm port connections). In other words we can keep on treating arrays and these other global things the way we always did before. On the other hand this can make our code more complicated because if we need to use these kinds of resources in our functions we must be careful how we share them throughout our programs.

Here is a nonsense example:

'fill and display an array with a random quantity of
'numbers
dim numbers(100)
count = fillRandomly(100)
for x = 1 to count
    print numbers(x)
next x
end
 
function fillRandomly(limit)
    fillRandomly = int(rnd(1)*limit)+1
    for x = 1 to fillRandomly
        numbers(x) = int(rnd(1)*fillRandomly)+1
    next x
end function

When we fill in the array named numbers() inside the function fillRandomly(), we are filling the same array named numbers() that we dimension in the start of the example. This is why we then have no trouble printing out the contents of that array.

As an example of a global handle, examine this short program:

'show how to use functions with handles open "global handles" for graphics as #draw
print #draw, "down"
x = drawLine(50, 50, 150, 50)
x = drawLine(50, 100, 150, 100)
x = drawLine(50, 50, 50, 100)
x = drawLine(150, 50, 150, 100)
input r$
 
function drawLine(a, b, c, d)
    print #draw, "line "; a; " "; b; " "; c; " "; d
end function

 

See how in this example it shortens and simplifies our code? Instead of duplicating that style of print statement for each line we draw all we need to do is use the drawLine() function.

Since functions always return some value, even if it is zero as in this case, we must do something with that value. So we assign the value to x.

What could be

What we've looked at so far is natural for converting existing Liberty BASIC programs, but it does tie functions to specific windows. To remove this dependency, it is possible that the function calling mechanism could be extended to make this possible:

x = drawLine(#draw, 50, 50, 150, 50)

and

function drawLine(#graphics, a, b, c, d)
    print #graphics, "line "; a; " "; b; " "; c; " "; d
end function

This form is workable and it is visually satisfying, but I haven't found an acceptable solution for doing this with arrays. I can imagine something obvious like the following but I'm not sure I like it:

'fill and display an array with a random quantity of
'numbers
dim numbers(100)
count = fillRandomly(numbers(),100)
for x = 1 to count
    print numbers(x)
next x
end
 
function fillRandomly(array(), limit)
    fillRandomly = int(rnd(1)*limit)+1
    for x = 1 to fillRandomly
        array(x) = int(rnd(1)*fillRandomly)+1
    next x
end function

The sort of functionality we see in this example would let us use a single function with more than one array.

Another feature which would be nice is local arrays. For example if an array is dimensioned within a function that array might be considered local, even if it has the same name as a global array. Of course another way to do this would be to treat only arrays explicitly declared as global to be visible to functions.

As for requiring that we must always do something about the return value of a function, I was thinking we could do something like:

call drawLine(x1, y1, x2, y2)

or just this (no parenthesis needed):

drawLine x1, y1, x2, y2

These would allow us to capitalize on the existing user function mechanism without the expense of implementing sub-programs as a completely parallel feature as in some other BASICs.

I'm open to suggestions about these things, but not at all unwilling to release v2.0 with user functions support implemented pretty much as it is now. New features can always be added in later releases. How does the Liberty BASIC community feel about emulating the way QBASIC does these things?

I want to open this up for discussion, so I have added a page to the Liberty BASIC CoWeb where people can have an opportunity to give feedback about what I've presented.


BEGINNER'S TUTORIAL ON USER DEFINED FUNCTIONS

 

Carl has dealt with user defined functions in detail. The following little tutorial is meant to explain how to set up and use these user defined functions in the simplest terms and format. It only deals with the most fundamental parts of writing and using our own functions.

We use functions in Liberty BASIC all of the time. Many functions are an integral part of the language. One function is the ABS(x) function. It returns the absolute value (the value without regard to sign) of a number. Here it is in action:

a = ABS(-3)

Now, the variable "a" holds the value "3". We can also use a variable as a value inside of the brackets:

x = -3
a = ABS(x)

The variable "a" again holds the value "3". Liberty BASIC does its work in the background, and all we see is the result. Let's pretend that we can "see" the ABS function. It might look like this:

function ABS(num)
    IF num >= 0 THEN 
      ABS = num
    ELSE
      ABS = 0 - num
    END IF
  end function

A function may receive a parameter, or a set of parameters, it performs an action, or a set of actions, and then it may return a value. The ABS function required only one parameter, and it returned a numeric value. The LEFT$ function requires two parameters, and it returns a string value:

new$ = LEFT$(text$, length)

To use a function, we must list the proper number and type of parameters. If we tried the following, it would halt with an error message:

new$ = LEFT$(text$)

It is missing a parameter, and that generates an error. The next one also generates an error:

new$ = LEFT$(length,text$)

It contains the proper number of parameters, and they are of the proper type, but since they are not in the proper order, the function perceives the parameters to be of the wrong type.

Liberty BASIC 2.0 allows us to define our own functions. Carl has listed the benefits in his article. Let's talk about creating our own functions.


CREATING OUR OWN FUNCTIONS

Let's start out by structuring our code for maximum ease of use. We can do that by grouping all of our functions together, possibly at the bottom of the source code. This will make them really easy to find. Carl has added function-names to his branch label search window, so we can find them without too much trouble, but grouping them together makes it easier, still.

Carl says:

If you put functions before your code you will need to goto or gosub to your code after the functions like so:

dim arrays 'this needs to happen right up front unless you use redim
goto [getStarted]
 
{function defs}
 
[getStarted]

Without that jump over the functions, the program will just stop. Look at fform20.bas for an example of this in action.

Notice also that array dimensioning needs to happen up front unless you use redim. I intend to fix this before LB2.0 is released.


The syntax for a function requires the word "function" to start, followed by the name of the function. Avoid reserved words, such as Liberty BASIC commands and functions. If you try to use reserved words, the compiler will halt with a syntax error. You couldn't use a function called "print" for this reason, but you could use "print1". Just as variable names are case sensitive, function names are case sensitive. A function named Carl() is not the same as a function named carl() or a function named cArl(). Here is the syntax for creating a function:

  function FunctionName(parameter1,parameter2...)
    {basic code routine}
    FunctionName = (value)
  end function

Note from Carl: "Also, until I can get more specific, it's best to keep the [inputLoop] of a Liberty BASIC program outside of functions."

Let's write a function. Let's take in a numeric parameter, square it within the function, and return the square.

  function Square(num)
    Square = num * num
  end function

The return from a function is contained in a variable that is identical to the name of the function. Since we have called our function "Square()", the returned value will be contained in "Square".

A number multiplied by itself will give the square of the number. Another way to define "square" is to take a number to the power of 2. In Liberty BASIC, we would write num^2. Here is the same function, using the power of two, instead of multiplying a number by itself:

  function Square(num)
    Square = num^2
  end function

Does it matter which way we write the code inside of the function? No! As long as the routine works properly, it doesn't matter at all to the calling routine. The calling routine is only concerned with the result of the function. Let's take a look at how we will use our new function within a program.


USING OUR OWN FUNCTIONS

We can call upon our function as often as we want. Here is an entire, simple program that uses ourSquare function:

  x = 4
  print Square(x)
 
  function Square(num)
    Square = num * num
  end function

 

If you copy and paste the above lines into alpha #4, you will see this output on the screen:

16

Run the following code, which adds another function call:

  x = 4
  print Square(x)
  x = 5
  print Square(x)
 
  function Square(num)
    Square = num * num
  end function

This time, the output on the screen would look like this:

  16
  25

We can use our own functions in the same ways that we use Liberty BASIC functions. In LB:

print ABS(-3) + 1

results in output:

4

Let's see how that works with user defined functions:

  x = 4
  print Square(x) + 1
 
  function Square(num)
    Square = num * num
  end function

OUTPUT:

  17

If a function returns a value, we can set a variable equal to that value, allowing us to store it for later use. The value of Square(x) can be changed if the function is called again in the program, but we can conserve the value by placing it into the variable, "sq". Example:

  x = 4
  sq = Square(x) + 1
 
  print "The value in Square is now ";Square(2)
  print "The value of sq is equal to ";sq
 
  function Square(num)
    Square = num * num
  end function

OUTPUT:

    The value in Square is now 4
    The value of sq is equal to 17


STRINGS IN USER DEFINED FUNCTIONS

If a function is to return a string, rather than a numeric value, then its name must end with a "$". This matches the way Liberty BASIC treats variables. A function named Carl$() will return a string value, while a function called Carl() will return a numeric value.

We already use string functions in Liberty BASIC. Here's an example:

print UPPER$("hello")

produces output:

HELLO

Let's write a simple string function:

    print Month$(date$())
    
    function Month$(string$)
        Month$ = LEFT$(string$,3)
    end function

OUTPUT:

    Mar

We can make that particular function even more efficient. Since we want it to return the 3-letter month value from the date$() function, we can put the date$() function within our user defined function. In that case, we don't need to pass ANY parameters!

print Month$()
 
function Month$()
        Month$ = LEFT$(date$(),3)
end function

OUTPUT:

    Mar

We can use our own functions in tandem with Liberty BASIC functions, just as we can use LB functions in tandom with one another. For instance, we might have something like this in Liberty BASIC:

    print upper$(word$("Carl Gundel",1))

OUTPUT:

    CARL

We can combine our own user defined functions and Liberty BASIC functions in just the same way. Here's a sample:

    print upper$(Month$())
 
    function Month$()
        Month$ = LEFT$(date$(),3)
    end function

OUTPUT

    MAR

The parameter types for a function can be a combination of strings and numerics, and parameter types need not match the type of the function itself. Here is an example of a function with a numeric return, whose parameters are all strings. This function is similar to LB's instr() function, except that it returns the last occurance of string 2 within string 1, rather than the first occurance.

  function lastSpot(string$,a$)
    value = len(string$)
    WHILE mid$(string$,value,len(a$))<>a$ and value > 0
      value = value -1
    WEND
    lastSpot = value
  end function

Here it is in action -- a numeric value is returned, while the parameters are both strings:

  print lastSpot("one two one","one")
 
  function lastSpot(string$,a$)
    value = len(string$)
      WHILE mid$(string$,value,len(a$))<>a$ and value > 0
        value = value -1
      WEND
    lastSpot = value
  end function

OUPUT:

  9


LOCAL VARIABLES AND INFORMATION HIDING

Carl has addressed the use of global and local variables in detail in his article. It is very important, so we'll talk just a little bit about it again here.

Look at the little program below. It may not look like it, but there are actually TWO variables called "name$". The contents of one do not affect the contents of the other, because one "name$" variable is local, contained within the function, ShowBob$(). If you print the contents of the variable, "name$" both from within the function, and outside of it, you will get different results. Also notice that the "name$" variable that is used outside of the function retains its value. It is exactly the same before and after the function is called, proving that the variable "name$" that is used within the function is separate from it's global look-alike.

    print "name$ at start is ";name$
    print "ShowBob$ return is ";ShowBob$()
    print "name$ after function ends is ";name$
    name$ = "Sam"
    print "name$ is now ";name$
    print "ShowBob$ return is ";ShowBob$()
    print "name$ is now ";name$
 
    function ShowBob$()
        name$ = "Bob"
        print "name$ inside function is ";name$
        ShowBob$ = name$
    end function

 

OUTPUT:

    name$ at start is
    name$ inside function is Bob
    ShowBob$ return is Bob
    name$ after function ends is
    name$ is now Sam
    name$ inside function is Bob
    ShowBob$ return is Bob
    name$ is now Sam

As Carl has already explained so well, this use of a local variable insures that we do not accidentally give a bad value to a variable that is used elsewhere in the program. Think of it as a sort-of "pre-debugging" technique!


Don't forget to visit the Liberty BASIC Community Web at:

http://www.libertybasic.com/CoWeb


Brosco and Alyce have written a Book for Liberty BASIC,

which is available in electronic form on a CDROM.

For details: http://alyce.50megs.com/sss/cd.htm


Newsletter written by: Brosco. Comments, requests or corrections to: brosco@orac.net.au Translated from Australian to English by an American: Alyce Watson. Thanks Alyce.