Functions & Psubs

By: Chris (fog) Jeffels & Kevin Picone



I N D E X:






What are they?


     PlayBasic supports both functions and psubs so what are they? What are the differences between the two? And most importantly how do we use them in our code?

     Well! Put simply both functions and psubs can be thought of as user defined commands. Or more specifically, a predefined sequence of commands that can be called at any time in other parts of your program. With a bit of planning, not only will they speed up development of your current project but also improve development time of future projects and indeed also make development easier. Quite quickly a library of functions or psubs can be created that could expand the already comprehensive PlayBasic command list.

     We will discuss the actual differences between functions and psubs later, as they are relatively small (but significant). Everything you see here applies to both functions and psubs. To use psubs simply replace FUNCTION and ENDFUNCTION with PSUB and ENDPSUB.

Top





The Basics


     Okay let's start by writing a basic function to print "Hello" on the screen (do not run it yet):

  
Function Show_Text()
  Text 10,10,"Hello"
EndFunction
  


     As you can see the function itself can be divided into 3 separate parts. For ease we'll refer to them as the header, the code sequence and the footer:


FUNCTION Show_Text() -the header
TEXT 10,10,"Hello" -the code sequence
ENDFUNCTION -the footer


     The Header - This consists of the FUNCTION command followed by the name of our function, in this case Show_Text followed by a set of brackets (more on these later).

     The Code Sequence - Here we put all the code we want to be run when we call the function from within our program. Here it's TEXT 10,10,"Hello"

     The Footer - Here the ENDFUNCTION command is used to signify the end of our function code sequence. The ENDFUNCTIONcommand can be followed also by a variable but again more on that later.

     Now just typing that function into the editor and running it will do nothing. All commands between the FUNCTION and ENDFUNCTION commands are ignored until you add a function call to your program. A function call is added by simply typing the name of the required function. So let's add that along with a WAITKEY command so we can see the result of our function:

  
  Show_Text()
  WaitKey
  
Function Show_Text()
  Text 10,10,"Hello"
  Sync
EndFunction
  


     So now try running that in the editor. Not terribly impressive so far but that's the very basics of functions covered. Simple enough isn't it.

Top





Passing Variables


     Now that alone would have very limited uses but wouldn't it be good if we could pass information into our function so that it would display any text we wanted? Well we can. Remember those brackets we mentioned earlier? Well that's where we put any variables we want to pass to our function. So let's do a quick modification to our existing function:


  
  Show_Text("Hello")
  WaitKey
  
Function Show_Text(mytext$)
  Text 10,10,mytext$
  Sync
EndFunction
  



     Now in our function call we've added a string of text and in our function header we have added a variable name between the brackets. This variable will be used to store the string passed to the function by the function call. So taking our first function call as an example this in effect this means:


FUNCTION Show_Text(mytext$="Hello") do not type this!


      So as an exercise let us expand on that a little further. We'll change it so that we can also specify where we want the text to appear on screen by also passing x and y co-ordinates into the function:


  
  Show_Text(10,10,"Hello")
  Show_Text(20,20,"World")
  Sync
  WaitKey
  
Function Show_Text(x, y, mytext$)
  Text x,y,mytext$
EndFunction
  



     Also note that the values we pass into a function can themselves be variables. eg:



x=10
y=20
showme$="Hello"
Show_Text(x,y,showme$)


      So far all this really does, is recreate the TEXT command, so a challenge for you might to try adding extra variables to control things like text centering or colour. You'll soon see just how flexible functions can be.



      The following two sections (Passing Arrays & Passing Types) cover some of the slightly more advanced features within PlayBasic so feel free to skip over them to the Returning Values section and returning when you feel you've grasped the basics

Top





Passing Arrays


      One of the areas where PlayBasic excels over some other Basic languages is the ability to pass arrays and indeed types into functions or psubs by pointers (whats an array ?). Possibly the best way to see how this works, is to look at another example. Let us create a simple function to fill any string array with "Hello":


  
  Dim My_Array$(10)
  Fill_Array(My_Array$())
  
Function Fill_Array(Temp_Array$())
  Size=GetArrayElements(Temp_Array$(),1)
  For t=0 To Size
     Temp_Array$(t)="Hello"
  Next t
EndFunction
  


     As you can see the function call Fill_Array(My_Array$())passes the name of the array we want to fill into the function. In the function header this is then assigned to the Temp_Array$ so everywhere we use that variable, the actions will actually be carried out on our passed array.
(The function itself works by getting the size of the array using GETARRAYELEMENTS and then looping through them filling each one with the "Hello" string.)

     Note also that unlike the arrays used in our main code, the temporary arrays used in functions, do not have to be dimensioned first using the DIM command.


     Of course it is also perfectly possible to pass an array index to the function as well. eg:

  
  Dim My_Array$(10)
  Fill_Array(My_Array$(),3)
  
Function Fill_Array(Temp_Array$(),index)
  Temp_Array$(index)=Hello
EndFunction
  



     The only limitation on this is that the array used in the function must have the same number of dimensions as the array passed into it. For example the following would not work as the passed array has 2 dimensions but the function array only has one:

  
  Dim My_Array$(10,10)
  Add_Hello(My_Array$())
  
Function Add_Hello(Temp_Array$())
  Temp_Array$(1)=Hello
EndFunction
  


Top





Passing Types


      The next logical step from being able to pass arrays to functions is the ability to pass types by pointers and that's exactly what you can do in PlayBasic.

      The way this is done is very similar to what we have just done with passing arrays. Again let us look at a simple example starting by defining a typed array:

  
  Type MyObject
     Width As Integer
     Height As Integer
     Volume As Integer
  EndType
  Dim MyBox(10As MyObject
  
  Calculate_Volume(MyBox().MyObject,1,10,20)
  
Function Position_Object(Temp_Array().MyObject,ind,w,h)
  Temp_Array(ind).Width=w
  Temp_Array(ind).Height=h
  Temp_Array(ind).Volume=w*h
EndFunction
  



     The only real thing to note here is the naming convention for both the passed Box and the functions Temp_Array variables. Both are followed by the name of the type definition (in this case MyObject) and as with the number or array dimensions these must be the same.


     By using another slightly more complicated example, with objects more familiar to games, it is easy to see just how useful this can be:


  
  Type MyObject
     X As Integer
     Y As Integer
     Spr As Integer
  EndType
  
  Dim Alien(10As MyObject
  Dim Bullet(10As MyObject
  
  Get_Sprites(Alien())
  Get_Sprites(Bullet())
  
  Position_Object(Alien(),1,500,400)
  Position_Object(Bullet(),4,200,250)
  
  
Function Get_Sprites(Temp_Array().MyObject)
  For t=1 To 10
     Temp_Array(t).Spr=NewSprite(Xpos,Ypos,IMage)
  Next t
EndFunction
  
Function Position_Object(Temp_Array().MyObject,ind,xpos,ypos)
  Temp_Array(ind).X=xpos
  Temp_Array(ind).Y=ypos
  PositionSprite Temp_Array(ind).Spr,xpos,ypos
EndFunction
  
  


Top





Returning values


      Up to now we've only dealt with passing variables to functions but you can also have a function return a variable (typically the result of a calculation). For Example:


  
  New_Value=Add(3,5)
  
Function Add(a,b)
  c=a+b
EndFunction c
  



      Here we are passing two values into the function (3 and 5). They are then added together and the result stored in "c". This is then passed back to the main code. When a value is passed out of a function that we want to keep, we need to store it in a new variable, so in this case the result is stored in New_Value


We can also use the resulting value directly with another command. eg:

PRINT Add(3,5)


It is also worth noting that functions can easily be combined together. eg:

  
  Show_Text(20,20,Str$(Add(3,5)))
  WaitKey
  
Function Show_Text(x, y, mytext$)
  Text x,y,mytext$
EndFunction
  
Function Add(a,b)
  c=a+b
EndFunction c
  



     As you can see only being able to return one value from a Function or Psub can be somewhat restrictive. Where PB excels over many other basic languages is in its ability to return more than one value. In the following example the function returns both the volume and the mass of a steel block of given dimensions. All that needs to be done to return more than one value is to separate them with a comma both after the ENDFUNCTION command and also at the function call.


  
  My_Volume,My_Mass=Block_Properties(1,2,4,7800)
  
  Print "Volume = "+Str$(My_Volume)+" m^3"
  Print "Mass = "+Str$(My_Mass)+" Kg"
  Sync
  WaitKey
  
Function Block_Properties(Width,Length,Height,Density)
  Volume=Width*Length*Height
  Mass=Volume*Density
EndFunction Volume,Mass
  


Top





Variable Scope


     The other thing to consider with functions is how they handle normal variables. Put simply variables inside functions are local to the function they were created in. So a normal variable created outside of a function can have a very different value inside the function. Sounds complicated at first but it's not. Here's a basic example:


  
  c=3
  Print c
  Test()
  Print c
  
Function Test()
  c=999
EndFunction
  



      As you can see the variable c=3 in the main program, but in the function c=999. However the "c" variable in the function is completely different to the "c" variable in the main code. The "c" in the function is LOCAL and only has a value of 999 inside that function. We can prove this by running the code. You will see that the result of the second PRINT statement is "3" even after the function call.

     So what happens if we need to be able to access our variables inside functions? Well we simply make them GLOBAL. This means they have the same value in the main code as they do inside all of your functions. So simply by adding a global definition to the previous example our second PRINT statement will now output "999" eg:

  
  Global c
  c=3
  Print c
  Test()
  Print c
  
Function Test()
  c=999
EndFunction
  



      Another thing to note about variables is that Arrays are all global by default.

      So what do you do if you want a variable to retain values between a function call? Well again this is catered for with the STATIC command. Try running the following example both with and without the STATIC command.


  
  For t=1 To 10
     Print Count()
  Next t
  
  Sync
  WaitKey
  
Function Count()
  Static total
  total=total+1
EndFunction total
  



     As you'll see with the STATIC command in place, the total variable retains it's value between function calls resulting in the sequence 1-10 being displayed. Without the command however, the total variable loses it's value between calls meaning it is continually reset to zero.

Top





Recursion


     Recursion is simply a fancy name for being able to call a function from within itself. Which sounds a lot more confusing than it really is as the following example will hopefully show.


  
  Countdown(10)
  Sync
  WaitKey
  
Function CountDown(num)
  Print num
  Dec num
  If num<>0
     CountDown(num)
  Else
     Print "Boom"
  EndIf
EndFunction
  



     Although fairly simple it is important to consider how recursive function calls are handled with each function call being effectively nested inside the previous call. Again this sounds more complicated than it really is. Run the following example and watch the num variable to see when the various functions are called and exited.

  
  
  Countdown(10)
  
  Sync
  WaitKey
  
Function CountDown(num)
  Dec num
  Print Make$(" ",10-num)+"Function Start: num="+Str$(num)
  If num<>0 Then CountDown(num)
  Print Make$(" ",10-num)+"Function End:   num="+Str$(num)
EndFunction
  


Top





Function or Psub?


      On surface they seems to be much the same, but there are a couple real differences between functions and psubs. The two main one's would be that variables used within PSUB's retain their values upon leaving the sub. So they're always Static. Another would be that unlike Functions (where we can exit at any time via ExitFunction), you can't exit a psub at anytime. These have to be exited through their closing EndPSub statement.

      Lets take a look at some examples.. First we'll to show, we'll examine how variables Behave in a PSUB and FUNCTION.


  
; Call our test function 10 times
  For lp =1 To 10
     Result=TestFunction()
  Next
  Print Result
  
; Call our test sub 10 times
  For lp =1 To 10
     Result=TestSub()
  Next
  Print Result
  Sync
  WaitKey
  
Function TestFunction()
; delcare this LOCAL variable.
; Each time PB enters the function this  and add one to it
  MyVariable = MyVariable+1
EndFunction MyVariable
  
Psub TestSub()
; delcare this LOCAL variable and add one to it
  MyVariable = MyVariable+1
EndPsub MyVariable
  
  



      If you try this code in the editor, you'll notice they return two different results. This is because each time the function is called, all the variables within the function are initialized to zero. So each time we call a function it's always in a fresh initialized state. PSUB's on the other hand don't do this. Variables inside them retain their values/strings between calls. Which can useful if we need to the psub to remember something between calls, and we don't wish to use a globals.


      Lets look a second difference exiting from a function early.


  
  Print Find_Letter("C")
  Sync
  WaitKey
  
Psub Find_Letter(n$)
  If n$="A" Then x=1
  If n$="B" Then x=2
  If n$="C" Then x=3
  If n$="D" Then x=4
  If n$="E" Then x=5
EndPsub x
  



      As you can see, in this example the code will run though all five IF statements before reaching the ENDPSUB and returning the required value "x". Now with a FUNCTION we can exit as soon as we find a match. eg:

  
  Print  Find_Letter("C")
  Sync
  WaitKey
  
Function Find_Letter(n$)
  If n$="A" Then Exitfunction 1
  If n$="B" Then Exitfunction 2
  If n$="C" Then Exitfunction 3
  If n$="D" Then Exitfunction 4
  If n$="E" Then Exitfunction 5
EndFunction result
  
  



      In larger functions you can see how this could save us a considerable amount of time. Unfortunately there is a trade off for this. FUNCTION's generally run marginally slower than PSUB's so unless you need to exit part way through a FUNCTION it is probably best to use a PSUB.

Top





General Uses


     All the examples we've done have been very basic but they should give you an idea of how easy it is to build up a library of useful functions that could be reused over many projects. eg:

  
  TypeWriter("Hello this is my typewriter function")
     WaitKey
     
Function TypeWriter(mytext$)
  For t=1 To Len(mytext$)
     Text (t*10),20,Mid$(mytext$,t,1)
     Wait 100
     Sync
  Next t
EndFunction
  



      In theory you can put as much or as little into a function as you like. You could put just about your whole program into one big function if you wanted to. Fairly pointless, but possible.

      In reality the larger and more complicated functions become, the less reusable they are. Also in an effort to make your functions more flexible you may make them in fact too cumbersome to use. For instance you will soon tire of specifying a long list of variables to be passed every time you type a function call. Only you can really decide when increased flexibility becomes over complication but with a little thought it should be possible to strike a comfortable balance.

Top



 
Related Info: ArrayBasics | Global | Local | Loops | Operators | pointer | ProgramLayout | Static | Types | Variables :
 


(c) Copyright 2002 - 2024 - Kevin Picone - PlayBASIC.com