Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Use of Functions
#21
(05-21-2025, 12:11 PM)mdijkens Wrote: Example showing difference passing parameter variables by reference or by value:
Code: (Select All)

x = 5: x$ = "ABC"
Print "By Reference:"
Print "Checkfunction returns "; CheckFunction$(x, x$)
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$

x = 5: x$ = "ABC"
Print "By Value:"
Print "Checkfunction returns "; CheckFunction$((x), "a" + x$)
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$

Function CheckFunction$ (x, x$)
  x = x * 3: x$ = x$ + "DEF"
  CheckFunction$ = "hello"
End Function
Good example but it can bring some confusion:
I prefer to show variables out and into procedure...
Code: (Select All)

x = 5: x$ = "ABC"
Print "By Reference:"
Print "Checkfunction returns "; CheckFunction$(x, x$)
Print
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$

x = 5: x$ = "ABC"
Print "By Value:"
Print "Checkfunction returns "; CheckFunction$((x), "a" + x$)
Print
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$

Function CheckFunction$ (x, x$)
    Xp = CsrLin: Yp = Pos(0)
    Locate Xp + 1, 1
    x = x * 3: x$ = x$ + "DEF"
    Print "in Function: x = "; x; "X$= "; x$
    Locate Xp, Yp
    CheckFunction$ = "hello"
End Function

but it brings a hidden issue... 
What issue?
the second parameter of the Function is named X$ like the global variable X$ but their are 2 different variables... in the execution the compiler uses a different variable into function to work with a string... so when you modify X$ in Function you modify that is not the global X$ but "a"+X$... so when the value is popped out at the end of the procedure it has not been put into global X$ because it is "a"+X$. If you delete the string "a" added to X$ in the calling of function you'll observe that the global X$ has been changed. 
And so?
The correct way to pass string for value is (X$) and not X$...you get global X$ unchanged because you pass more than X$ as parameter, so the changement cannot be put into global X$ after exiting Function.


The confounding issue is to use the same name for parameters of FUNCTION and the global variables. We must use only the same type of variable, not the same name.
Thanks for focusing on this aspect of passing parameter by reference. A composite value passed have no return horse.
here your code modified to show better the issue:

Code: (Select All)

Color 0, 3
x = 5: x$ = "ABC"
Print "By Reference:"
Print "Before CheckFunction x="; x
Print "Before CheckFunction x$="; x$
Print "Checkfunction returns "; CheckFunction$(x, x$)
Print
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$

Color 9, 2
x = 5: x$ = "ABC": Y$ = "000"
Print "By Value:"
Print "Before CheckFunction x="; x
Print "Before CheckFunction x$="; x$
Print "Before CheckFunction Y$="; Y$

Print "Checkfunction returns "; CheckFunction$((x), Y$ + x$)
Print
Print "After CheckFunction x="; x
Print "After CheckFunction x$="; x$
Print "After CheckFunction Y$="; Y$
End
Function CheckFunction$ (x, x2$)
    Xp = CsrLin: Yp = Pos(0)
    Locate Xp + 1, 1
    x = x * 3: x2$ = x2$ + "DEF"
    Print "in Function: x = "; x; "X2$= "; x2$
    Locate Xp, Yp
    CheckFunction$ = "hello"
End Function

here screenshot of output
[Image: immagine-2025-05-21-223807334.png]
Reply
#22
Parameters "by reference" and "by value" have already been mentioned.  In case this is useful to the discussion, here are just a couple of silly examples:

By reference:

   

By value:

   
Reply
#23
(05-21-2025, 11:35 AM)bplus Wrote: SMcNeill: "I'm not certain what "unless you tell them to" refers to"

It refers to exactly what you showed in your example:
FUNCTION foo (x)
   foo = 2 * x
   X = x + 1  ' <<<  here Steve told the function to change the variable! not normally done with functions
END FUNCTION
This way, nothing happens because the function exits before x = x + 1.

Code: (Select All)

Dim As Integer x

x = 4
Print Using "Ergebnis: ###.###"; foo(x)

Function foo (x)
  foo = 2 * x
  x = x + 1
End Function

This makes sense.

Code: (Select All)


Dim As Integer x

x = 4
Print Using "Ergebnis: ###.###"; foo(x)

Function foo (x)
  x = x + 1
  foo = 2 * x
  'x = x + 1
End Function

[Image: Funktion-Anordnung-Falsch.jpg]
Reply
#24
Code: (Select All)
Function CheckFunction$ (x, x$)
    x = x * 3: x$ = x$ + "DEF"
End Function
something like this puts off enough people from this basic dialect.  they can't understand why qb64pe cannot adjust with something that could be optional to use.  such as:

Code: (Select All)
Function CheckFunction (x, x$) As String
    x = x * 3: x$ = x$ + "DEF"
    Result = x$ + Trim$(Str$(x))
End Function
(the code just above should run in freebasic.)

people more used to visual basic than quickbasic dislike the stupid type sigils.  on the other hand, the decision was unpopular for those of us older hangers-on.  in 2005 to force "return" to work like in c and "all those other" languages.  disallow "gosub" to be used ever again.

that's why instead i propose a keyword borrowed from pascal/delphi.  which doesn't need type sigil because it would be built in.  allow people to choose which style they prefer.

the topic starter is better off continuing to program how he revealed.  use subprograms only.  but be careful with the arguments that are changed inside a subprogram.  document which argument will be changed inside a subprogram.
Reply
#25
(05-22-2025, 03:30 PM)hsiangch_ong Wrote:
Code: (Select All)
Function CheckFunction$ (x, x$)
    x = x * 3: x$ = x$ + "DEF"
End Function
something like this puts off enough people from this basic dialect.  they can't understand why qb64pe cannot adjust with something that could be optional to use.  such as:

Code: (Select All)
Function CheckFunction (x, x$) As String
    x = x * 3: x$ = x$ + "DEF"
    Result = x$ + Trim$(Str$(x))
End Function
(the code just above should run in freebasic.)

people more used to visual basic than quickbasic dislike the stupid type sigils.  on the other hand, the decision was unpopular for those of us older hangers-on.  in 2005 to force "return" to work like in c and "all those other" languages.  disallow "gosub" to be used ever again.

that's why instead i propose a keyword borrowed from pascal/delphi.  which doesn't need type sigil because it would be built in.  allow people to choose which style they prefer.

the topic starter is better off continuing to program how he revealed.  use subprograms only.  but be careful with the arguments that are changed inside a subprogram.  document which argument will be changed inside a subprogram.

Yes, thanks hsiangch_ong.
I'm afraid some of the replies were a bit too much for my little brain to cope with. 
I grew up through Locomotive and GW basic, with Subs my main "goto" (pardon the pun), and I find I can do most of the things I need with this, 
with the help of the newer graphics, sound and file-handling features etc. 
I haven't seen any replies that would persuade me to change at this late stage. 
Thanks to those who replied. I appreciate your work and help. You've helped to clear some of the mystery around this topic for me.
Of all the places on Earth, and all the planets in the Universe, I'd rather live here (Perth, Western Australia.) Big Grin
Please visit my Website at: http://oldendayskids.blogspot.com/
Reply
#26
Here is my latest Function:
Code: (Select All)
_Title "BMI for males" ' b+ 2025-05-22

Print BMI(67, 157) ' matches other sources for me 5/22/2025 24.59 Normal!

Function BMI (HeightInches, Lbs)
    BMI = Lbs / (HeightInches ^ 2) * 703
End Function

I use to be well into the obese category, down 90 lbs now!
b = b + ...
Reply
#27
Wow, I'm surprised how popular this thread is! 

The one thing that has confused / surprised me about QB64PE's functions is you can't declare the function's return type with the type name, e.g., the IDE won't accept:

Function MyFunction(Param1 As Integer, Param2 As Integer) As String

instead you have to declare it using the type symbol, e.g., 

Function MyFunction$(Param1 As Integer, Param2 As Integer)

It doesn't bother me enough to care, but it did confuse me at first and it just seems inconsistent with what I'm used to with VB6, VBA, etc. (I don't remember whether the original QuickBasic was like that.)
Reply
#28
You just made a good point supporting the use of Subs over Functions!

Try and return an array or structure/UDT with a function (in QB64).

OTOH normally you use a sub to draw something but when reviewing my handy Subs and Functions in my toolbox last night (BTW Subs outnumber Functions maybe 5 to 1 !) I found a Function that draws a Sky for background according to a light percentage from 0 to 100. It's a function because it makes a background image that you can reuse in mainloop for drawing same background to clear screen and update animation action. So it returns the handle to that image which needs to be shared with Main program to use in loop.

Here is quick demo test (not using it in a loop over and over again for a background for an animation) just comparing different light settings:
Code: (Select All)
_Title "DrawSky&(light) test" 'b+ 2025-05-22
Screen _NewImage(800, 600, 32)
_ScreenMove 250, 60
For i = 0 To 100 Step 12.5
    hdl& = DrawSky&(i)
    _PutImage , hdl&, 0
    _Title "DrawSky&(light) test, light at" + Str$(i) + "%"
    _Display
    Sleep
Next

Function DrawSky& (light As Long) ' light = 0 to 100 as percent
    ' needs MidInk~&() Function
    Dim As _Unsigned Long saveColor, c
    Dim As Long i, rtn, saveDest
    Dim r, rn, xx, yy, lite
    lite = 2 * light
    saveDest = _Dest
    saveColor = _DefaultColor(saveDest)

    rtn = _NewImage(_Width, _Height, 32)
    _Dest rtn&
    For i = 0 To _Height - 1
        c = midInk(.75 * lite + 10, .75 * lite + 5, 35 + .75 * lite, 25 + lite, lite, 55 + lite, i / (_Height - 1))
        Line (0, i)-(_Width, i), c
    Next
    'stars only in low lite
    If lite <= 100 Then
        For i = 1 To _Width * _Height / 1500
            rn = Rnd: xx = Rnd * _Width: yy = Rnd * _Height
            If rn < .01 Then
                For r = 0 To 2 Step .5
                    Circle (xx, yy), r, _RGB32(185, 185, 185)
                Next
            ElseIf rn < .2 Then
                Circle (xx, yy), 1, _RGB32(185, 185, 185)
                PSet (xx, yy), _RGB32(185, 185, 185)
            Else
                PSet (xx, yy), _RGB32(185, 185, 185)
            End If
        Next
    End If
    _Dest saveDest
    Color saveColor
    DrawSky& = rtn
End Function

Function midInk~& (r1%, g1%, b1%, r2%, g2%, b2%, fr##)
    midInk~& = _RGB32(r1% + (r2% - r1%) * fr##, g1% + (g2% - g1%) * fr##, b1% + (b2% - b1%) * fr##)
End Function
b = b + ...
Reply
#29
Also reviewing my handy Functions last night I ran into a couple of very useful ones PLUS they reminded me of PhilOfPerth.

So here they are with a demo for @PhilOfPerth Smile
Code: (Select All)
Test$ = "PhilOfPerth"
Print "Left of "; Test$; " is "; LeftOf$(Test$, "Of")
Print "Right of "; Test$; " is "; RightOf$(Test$, "Of")

Function LeftOf$ (source$, of$)
    If InStr(source$, of$) > 0 Then
        LeftOf$ = Mid$(source$, 1, InStr(source$, of$) - 1)
    Else
        LeftOf$ = source$
    End If
End Function

' update these 2 in case of$ is not found! 2021-02-13
Function RightOf$ (source$, of$)
    If InStr(source$, of$) > 0 Then
        RightOf$ = Mid$(source$, InStr(source$, of$) + Len(of$))
    Else
        RightOf$ = ""
    End If
End Function
b = b + ...
Reply
#30
Here is 34 very handy Functions in less than 300 LOC
Code: (Select All)
' ======================================================================= IFFs of a Type 2023-11-03
Function IfS$ (Bool&, tru$, fals$) ' IF Boolean Return True$ else False$
    If Bool& Then IfS$ = tru$ Else IfS$ = fals$
End Function

Function IfL& (Bool&, tru&, fals&) ' IF Boolean Return True& else False&
    If Bool& Then IfL& = tru& Else IfL& = fals&
End Function

Function IfD# (Bool&, tru#, fals#) ' IF Boolean Return True# else False#
    If Bool& Then IfD# = tru# Else IfD# = fals#
End Function

Function IfUL~& (Bool&, tru~&, fals~&) ' IF Boolean Return True~& else False~&
    If Bool& Then IfUL~& = tru~& Else IfUL~& = fals~&
End Function

' ===================================================================================== Random stuff
Function rndI& (n1 As Long, n2 As Long) 'return an integer between 2 numbers
    Dim As Long l, h
    If n1 > n2 Then l = n2: h = n1 Else l = n1: h = n2
    rndI& = Int(Rnd * (h - l + 1)) + l
End Function

Function rndR (n1, n2) 'return real number (_single, double, _float depending on default / define setup)
    rndR = (n2 - n1) * Rnd + n1
End Function

Function rndCWI (center, range) 'center +/-range  weights to center
    Dim As Long halfRange, c
    halfRange = Int(range) + 1 'for INT(Rnd)  round range in case not integer
    c = Int(center + .5)
    rndCWI = c + Int(Rnd * (halfRange)) - Int(Rnd * (halfRange))
End Function

' 2023-01-20  tested
Function rndCW (C As Single, range As Single) 'center +/-range weights to center
    rndCW = C + Rnd * range - Rnd * range
End Function

Function rdir% ()
    If Rnd < .5 Then rdir% = -1 Else rdir% = 1
End Function

'===================================================================================== Handy numbers
Function dist# (x1%, y1%, x2%, y2%) ' hypot might be better
    dist# = ((x1% - x2%) ^ 2 + (y1% - y2%) ^ 2) ^ .5
End Function

Function min (a, b)
    min = -a * (a < b) - b * (b <= a)
End Function

Function max (a, b)
    max = -a * (a > b) - b * (b >= a)
End Function

Function map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
    map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
End Function

'======================================================================================= Color stuff
Function qb~& (n As Long)
    Select Case n
        Case 0: qb~& = &HFF000000
        Case 1: qb~& = &HFF000088
        Case 2: qb~& = &HFF008800
        Case 3: qb~& = &HFF008888
        Case 4: qb~& = &HFF880000
        Case 5: qb~& = &HFF880088
        Case 6: qb~& = &HFF888800
        Case 7: qb~& = &HFFCCCCCC
        Case 8: qb~& = &HFF888888
        Case 9: qb~& = &HFF0000FF
        Case 10: qb~& = &HFF00FF00
        Case 11: qb~& = &HFF00FFFF
        Case 12: qb~& = &HFFFF0000
        Case 13: qb~& = &HFFFF00FF
        Case 14: qb~& = &HFFFFFF00
        Case 15: qb~& = &HFFFFFFFF
    End Select
End Function

Function rclr~& ()
    rclr~& = _RGB32(rndI(64, 255), rndI(64, 255), rndI(64, 255), rndI(0, 255))
End Function

Function Plasma~& ()
    cN = cN + 1 ''Dim Shared cN, pR, pG, pB
    Plasma~& = _RGB32(127 + 127 * Sin(pR * cN), 127 + 127 * Sin(pG * cN), 127 + 127 * Sin(pB * cN))
End Function

Function midInk~& (r1%, g1%, b1%, r2%, g2%, b2%, fr##)
    midInk~& = _RGB32(r1% + (r2% - r1%) * fr##, g1% + (g2% - g1%) * fr##, b1% + (b2% - b1%) * fr##)
End Function

' ===================================================================================== String Stuff
Function LeftOf$ (source$, of$)
    If InStr(source$, of$) > 0 Then
        LeftOf$ = Mid$(source$, 1, InStr(source$, of$) - 1)
    Else
        LeftOf$ = source$
    End If
End Function

' update these 2 in case of$ is not found! 2021-02-13
Function RightOf$ (source$, of$)
    If InStr(source$, of$) > 0 Then
        RightOf$ = Mid$(source$, InStr(source$, of$) + Len(of$))
    Else
        RightOf$ = ""
    End If
End Function

Function strReplace$ (s$, replace$, new$) 'case sensitive  2020-07-28 version
    Dim p As Long, sCopy$, LR As Long, lNew As Long
    If Len(s$) = 0 Or Len(replace$) = 0 Then
        strReplace$ = s$: Exit Function
    Else
        LR = Len(replace$): lNew = Len(new$)
    End If

    sCopy$ = s$ ' otherwise s$ would get changed
    p = InStr(sCopy$, replace$)
    While p
        sCopy$ = Mid$(sCopy$, 1, p - 1) + new$ + Mid$(sCopy$, p + LR)
        p = InStr(p + lNew, sCopy$, replace$)
    Wend
    strReplace$ = sCopy$
End Function

Function strEnclose$ (s$, str1or2$)
    If Len(str1or2$) = 2 Then
        strEnclose$ = Left$(str1or2$, 1) + s$ + Right$(str1or2$, 1)
    ElseIf Len(str1or2$) = 1 Then
        strEnclose$ = str1or2$ + s$ + str1or2$
    Else
        Beep
    End If
End Function

Function StrCopies$ (NumberOfCopies&, S$) ' modified 2021-02-19 to match STRING$ args and for SUB AStringInsertAtNthPlace
    Dim i&, b$
    For i& = 1 To NumberOfCopies&
        b$ = b$ + S$
    Next
    StrCopies$ = b$
End Function

'Description: Use Item$() Function to treat strings like arrays without having to use an array structure.
' This function does not throw a fit if you ask for an item number (index) it does not have, it just returns an empty string.
' In QB64, Functions can't return arrays through the function name, but they can return strings that the Item$() function can
' translate like an an array index.  nItem numbers are the same as Counting numbers positive integers starting at 1.
'  eg Item7$ = Item$(CommaDelimitedString$, 7, ",") 'get 7th Item in string
Function Item$ (s$, nItem As Long, delimiter$)
    Dim c As Long, d As Long, lastd As Long
    If Len(s$) = 0 Then Item$ = "": Exit Function
    lastd = 1: d = InStr(lastd, s$, delimiter$)
    While d > 0
        c = c + 1
        If c = nItem Then
            Item$ = Mid$(s$, lastd, d - lastd): Exit Function
        Else
            lastd = d + 1: d = InStr(lastd, s$, delimiter$)
        End If
    Wend
    c = c + 1
    If c <> nItem Then Item$ = "" Else Item$ = Mid$(s$, lastd, Len(s$))
End Function

Function format$ (template As String, Source As String)
    Dim d, s, n, i, t$
    d = _Dest: s = _Source
    n = _NewImage(80, 80, 0)
    _Dest n: _Source n
    Print Using template; Val(Source)
    For i = 1 To 79
        t$ = t$ + Chr$(Screen(1, i))
    Next
    If Left$(t$, 1) = "%" Then t$ = Mid$(t$, 2)
    format$ = _Trim$(t$)
    _Dest d: _Source s
    _FreeImage n
End Function

Function xDP$ (x, DP)
    Dim test$, p As Long
    test$ = _Trim$(Str$(Int(x * 10 ^ DP) / 10 ^ DP))
    p = InStr(test$, ".")
    If p = 0 And DP <> 0 Then test$ = test$ + "." + String$(DP, "0")
    If p And DP = 0 Then test$ = Left$(test$, Len(test$) - 1)
    xDP$ = test$
End Function

Function commaD$ (n#, nDecPlaces%) 'only works right for double# type
    Dim place As Long, s$, front$, back$, func$
    func$ = _Trim$(Str$(n#))
    If Left$(func$, 1) = "-" Then s$ = "-": func$ = Mid$(func$, 2) Else s$ = ""
    place = InStr(func$, ".")
    If place = 0 Then place = Len(func$) + 1
    While place > 4
        func$ = Mid$(func$, 1, place - 4) + "," + Mid$(func$, place - 3)
        place = InStr(func$, ",")
    Wend
    'fix to nDecPlaces
    place = InStr(func$, ".")
    If nDecPlaces% Then
        If place Then
            front$ = Mid$(func$, 1, place)
            back$ = Mid$(func$, place + 1)
            If Len(back$) > nDecPlaces% Then func$ = front$ + Left$(back$, nDecPlaces%)
            If Len(back$) < nDecPlaces% Then func$ = front$ + Left$(back$ + String$(nDecPlaces%, "0"), nDecPlaces%)
        Else
            func$ = func$ + "." + String$(nDecPlaces%, "0")
        End If
    Else
        If place Then func$ = Mid$(func$, 1, place - 1)
    End If
    commaD$ = s$ + func$
End Function

'this might make a nice Money format
Function money$ (n#) 'only works right for double# type
    Dim place As Long, s$, front$, back$, b$
    b$ = _Trim$(Str$(n#))
    If Left$(b$, 1) = "-" Then s$ = "-": b$ = Mid$(b$, 2) Else s$ = ""
    place = InStr(b$, ".")
    If place = 0 Then place = Len(b$) + 1
    While place > 4
        b$ = Mid$(b$, 1, place - 4) + "," + Mid$(b$, place - 3)
        place = InStr(b$, ",")
    Wend

    'fix this for 2 places after decimal
    place = InStr(b$, ".")
    If place Then
        front$ = Mid$(b$, 1, place)
        back$ = Mid$(b$, place + 1)
        If Len(back$) > 2 Then b$ = front$ + Left$(back$, 2)
        If Len(back$) < 2 Then b$ = front$ + Left$(back$ + "00", 2)
    Else
        b$ = b$ + ".00"
    End If
    money$ = "$" + s$ + b$
End Function

Function DateTimeStamp$
    DateTimeStamp$ = Mid$(Date$, 7) + "-" + Mid$(Date$, 1, 2) + "-" + Mid$(Date$, 4, 2) + "_" + Mid$(Time$, 1, 2)
End Function

Function xStr$ (x, strng$) ' make x copies of string
    Dim i As Long, b$
    For i = 1 To x
        b$ = b$ + strng$
    Next
    xStr$ = b$
End Function

Function timeStr2Secs! (timeStr$)
    Dim hh As Long, mm As Long, s!
    hh = Val(Mid$(timeStr$, 1, 2))
    mm = Val(Mid$(timeStr$, 4, 2))
    s! = Val(Mid$(timeStr$, 7, 5))
    timeStr2Secs! = hh * 60 * 60 + mm * 60 + s!
End Function

Function secs2TimeStr$ (secs!)
    Dim hh As Long, mm As Long, s!, h$, m$, s$
    hh = secs! \ 3600
    mm = (secs! - hh * 3600) \ 60
    s! = (secs! - hh * 3600 - mm * 60)
    s! = Int(s! * 100) / 100
    h$ = Right$("00" + LTrim$(Str$(hh)), 2)
    m$ = Right$("00" + LTrim$(Str$(mm)), 2)
    s$ = Right$("00000" + LTrim$(Str$(s!)), 5)
    secs2TimeStr$ = h$ + ":" + m$ + ":" + s$
End Function

'load a file into a string from GUI Tools 1 TxtBoxes 2019-09-30
Function fileStr$ (txtFile$)
    Dim b$
    If _FileExists(txtFile$) Then
        Open txtFile$ For Binary As #1
        b$ = Space$(LOF(1))
        Get #1, , b$
        Close #1
        fileStr$ = b$
    End If
End Function

Function Join$ (arr() As String, delimiter$)
    Dim i As Long, b$
    For i = LBound(arr) To UBound(arr)
        If i = LBound(arr) Then b$ = arr(LBound(arr)) Else b$ = b$ + delimiter$ + arr(i)
    Next
    Join$ = b$
End Function
b = b + ...
Reply




Users browsing this thread: 7 Guest(s)