Author Topic: FUNCTION and SUB By Reference and By Value  (Read 3031 times)

0 Members and 1 Guest are viewing this topic.

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
FUNCTION and SUB By Reference and By Value
« on: August 06, 2019, 07:19:51 am »
In Steve's Posting about the Wiki being incorrect for FUNCTION

https://www.qb64.org/forum/index.php?topic=1531.0

I pointed out that arguments passed to a FUNCTION are passed By Reference by default and not by By Value.  That is to say, the argument gets modified in the FUNCTION and that modified value gets passed back to the calling argument.  This was a surprise to me, not to say rather a shock.

Incidentally, I never can remember which is By Reference and which is By Value (always have to look them up) – I'd rather they were called CHANGED and UNCHANGED respectively.

So, I discovered that if you don't want a FUNCTION to change your calling argument, you must pass the argument By Value, and this is done (QB64 methodology) by placing the argument within brackets, viz. if a FUNCTION F%() is called using a calling argument a% then

b% = F%(a%) - By Reference: a% will be modified

b% = F%((a%)) - By Value: a% will remain unchanged


So, I thought that I'd got that understood until I tried passing a String to a FUNCTION.  In this case, the String is always passed By Reference (and gets modified), no matter whether it is inside brackets or not.  Then I found that exactly the same thing happens with a SUB – a String is always modified by a SUB whether or not it is inside brackets.  I hope that you're still with me on this!

Code: QB64: [Select]
  1. 'Function / Sub By Val or By Ref
  2.  
  3. 'Now, the authentic definition is:
  4. 'When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.
  5. 'When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.
  6.  
  7. C1% = 3
  8. PRINT C1%; "- Numeric Original"
  9. CALL Cg((C1%))
  10. PRINT C1%; "- Numeric After SUB By Value"
  11. PRINT Ch%((C1%)); "- FUNCTION Result After FUNCTION By Value"
  12. PRINT C1%; "- Numeric After FUNCTION By Value"
  13. PRINT Ch%(C1%); "- FUNCTION Result After FUNCTION By Reference"
  14. PRINT C1%; "- Numeric After FUNCTION By Reference"
  15. CALL Cg(C1%)
  16. PRINT C1%; "- Numeric After SUB By Reference"
  17.  
  18. W2$ = "Change[to[Spaces"
  19. PRINT W2$; " - String Original"
  20. CALL Spacef((W2$))
  21. PRINT W2$; " - String After SUB By Value"
  22. PRINT Spaced$((W2$)); " - FUNCTION Result After FUNCTION By Value"
  23. PRINT W2$; " - String After FUNCTION By Value"
  24. PRINT Spaced$(W2$); " - FUNCTION Result After FUNCTION By Reference"
  25. PRINT W2$; " - String Result After FUNCTION By Reference"
  26. CALL Spacef(W2$)
  27. PRINT W2$; " - String Result After SUB By Reference"
  28.  
  29.  
  30. FUNCTION Ch% (C%)
  31.     C% = C% + 1
  32.     Ch% = 2 * C%
  33.  
  34. FUNCTION Spaced$ (X$)
  35.     I%% = INSTR(X$, "[")
  36.     WHILE I%% > 0
  37.         MID$(X$, I%%, 1) = " "
  38.         I%% = INSTR(X$, "[")
  39.     WEND
  40.     Spaced$ = X$
  41.  
  42. SUB Cg (C%)
  43.     C% = 3 * C%
  44.  
  45. SUB Spacef (X$)
  46.     X$ = X$ + "[zzz"

In the given code, there is a FUNCTION Ch%() which takes an Integer argument and a FUNCTION Spaced$() which takes a String argument, and there is a SUB Cg() which takes an Integer argument and a SUB Spacef() which takes a String argument.  The main block calls the FUNCTIONs and SUBs By Value and By Reference.

The code demonstrates what I have declared here:  a Numeric may be passed to a SUB or to a FUNCTION either By Reference (default) or By Value, but a String is always passed to a SUB or to a FUNCTION  By Reference.

Is this supposed to happen, and if so should a String behave differently to a Numeric?


From a queasy Qwerkey.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: FUNCTION and SUB By Reference and By Value
« Reply #1 on: August 06, 2019, 07:51:54 am »
I'd mentioned before that passing via parenthesis wasn't reliable.

For example, you can't even pass a User Defined Type with parenthesis, as you were attempting to do -- QB64 simply errors out:

Code: QB64: [Select]
  1.     x AS INTEGER
  2.     y AS INTEGER
  3.  
  4. DIM ex AS a
  5.  
  6. ex.x = 1: ex.y = 2
  7. result = foo((ex))
  8.  
  9. PRINT var.x, var.y
  10.  
  11. FUNCTION foo (var AS a)
  12.     var.x = var.x * 2
  13.     var.y = var.y * 3

The above won't work at all for you.

If you don't want to ever change the values of your parameters, the surefire method is to assign and pass the values through temp variables.

Code: [Select]
'Function / Sub By Val or By Ref

'Now, the authentic definition is:
'When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.
'When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.

C1% = 3
PRINT C1%; "- Numeric Original"
CALL Cg((C1%))
PRINT C1%; "- Numeric After SUB By Value"
PRINT Ch%((C1%)); "- FUNCTION Result After FUNCTION By Value"
PRINT C1%; "- Numeric After FUNCTION By Value"
PRINT Ch%(C1%); "- FUNCTION Result After FUNCTION By Reference"
PRINT C1%; "- Numeric After FUNCTION By Reference"
CALL Cg(C1%)
PRINT C1%; "- Numeric After SUB By Reference"
PRINT

W2$ = "Change[to[Spaces"
PRINT W2$; " - String Original"
CALL Spacef((W2$))
PRINT W2$; " - String After SUB By Value"
PRINT Spaced$((W2$)); " - FUNCTION Result After FUNCTION By Value"
PRINT W2$; " - String After FUNCTION By Value"
PRINT Spaced$(W2$); " - FUNCTION Result After FUNCTION By Reference"
PRINT W2$; " - String Result After FUNCTION By Reference"
CALL Spacef(W2$)
PRINT W2$; " - String Result After SUB By Reference"

END

FUNCTION Ch% (tempC%)
    C% = tempC%
    C% = C% + 1
    Ch% = 2 * C%
END FUNCTION

FUNCTION Spaced$ (tempX$)
    X$ = tempX$
    I%% = INSTR(X$, "[")
    WHILE I%% > 0
        MID$(X$, I%%, 1) = " "
        I%% = INSTR(X$, "[")
    WEND
    Spaced$ = X$
END FUNCTION

SUB Cg (tempC%)
    C% = tempC%
    C% = 3 * C%
END SUB

SUB Spacef (tempX$)
    X$ = tempX$
    X$ = X$ + "[zzz"
END SUB

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: FUNCTION and SUB By Reference and By Value
« Reply #2 on: August 06, 2019, 09:19:23 am »
Thanks
that is the sure solution
Quote
the surefire method is to assign and pass the values through temp variables.

but if we must call an external function of external library with string as value how can we pass string by value? Or is this my question an impossible case of coding?
 thanks to read and answer
Programming isn't difficult, only it's  consuming time and coffee

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: FUNCTION and SUB By Reference and By Value
« Reply #3 on: August 06, 2019, 09:41:55 am »
Thanks
that is the sure solution
but if we must call an external function of external library with string as value how can we pass string by value? Or is this my question an impossible case of coding?
 thanks to read and answer

Assign it to a temp value, whose whole purpose is just to pass the value and be changed and discarded

StringToKeep$ = "SteveIsAwesome"
temp$ = StringToKeep$
ExternalLibrary temp$ 'pass it temp$ so we won't care if it changes or not.
temp$ = "" 'and then blank the string to save memory after, if we want to.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: FUNCTION and SUB By Reference and By Value
« Reply #4 on: August 06, 2019, 10:07:53 am »
Another method (to using () or different TYPE set) is to make copies of the arguments passed inside the sub or function and work with the copies inside the sub or function leaving the variables passed untouched.

This method leaves it up to the maker of the procedure to effectively pass by ref or by value, so the user doesn't have to guess maybe 10 years down the line whether they have to protect values in call to procedure or not.

It is what Steve is doing inside the procedures of his examples above but by calling them "temp" variables in parameters of procedure definition is misleading IMHO.

before:
Code: QB64: [Select]
  1. _TITLE "GetDay$ function test."
  2.     PRINT "(don't forget 0's, just enter to quit)"
  3.     INPUT "  Enter yyyy-mm-dd date format: "; yymmdd$
  4.     yyyy = VAL(MID$(yymmdd$, 1, 4))
  5.     mm = VAL(MID$(yymmdd$, 6, 2))
  6.     dd = VAL(MID$(yymmdd$, 9, 2))
  7.     PRINT GetDay$(mm, dd, yyyy)
  8.     PRINT
  9. LOOP UNTIL yymmdd$ = ""
  10.  
  11. 'Steve (SAM) function fix for Ken
  12. FUNCTION GetDay$ (mm, dd, yyyy) 'use 4 digit year
  13.     'From Zeller's congruence: https://en.wikipedia.org/wiki/Zeller%27s_congruence
  14.     IF mm < 3 THEN mm = mm + 12: yyyy = yyyy - 1
  15.     century = yyyy MOD 100
  16.     zerocentury = yyyy \ 100
  17.     result = (dd + INT(13 * (mm + 1) / 5) + century + INT(century / 4) + INT(zerocentury / 4) + 5 * zerocentury) MOD 7
  18.     SELECT CASE result
  19.         CASE 0: Day$ = "Saturday"
  20.         CASE 1: Day$ = "Sunday"
  21.         CASE 2: Day$ = "Monday"
  22.         CASE 3: Day$ = "Tuesday"
  23.         CASE 4: Day$ = "Wednesday"
  24.         CASE 5: Day$ = "Thursday"
  25.         CASE 6: Day$ = "Friday"
  26.     END SELECT
  27.     GetDay$ = Day$
after:
Code: QB64: [Select]
  1. _TITLE "GetDay$ function test."
  2.     PRINT "(don't forget 0's, just enter to quit)"
  3.     INPUT "  Enter yyyy-mm-dd date format: "; yymmdd$
  4.     yyyy = VAL(MID$(yymmdd$, 1, 4))
  5.     mm = VAL(MID$(yymmdd$, 6, 2))
  6.     dd = VAL(MID$(yymmdd$, 9, 2))
  7.     PRINT GetDay$(mm, dd, yyyy)
  8.     PRINT
  9. LOOP UNTIL yymmdd$ = ""
  10.  
  11. 'Steve (SAM) function fix for Ken, bplus fix for user 10 years from now
  12. FUNCTION GetDay$ (mm, dd, yyyy) 'use 4 digit year
  13.     m = mm 'copy mm because we change it
  14.     y = yyyy 'copy yyyy because we may chnge that as well
  15.     ' dd is unchanged below
  16.     'From Zeller's congruence: https://en.wikipedia.org/wiki/Zeller%27s_congruence
  17.     IF m < 3 THEN m = m + 12: y = y - 1
  18.     century = y MOD 100
  19.     zerocentury = y \ 100
  20.     result = (dd + INT(13 * (m + 1) / 5) + century + INT(century / 4) + INT(zerocentury / 4) + 5 * zerocentury) MOD 7
  21.     SELECT CASE result
  22.         CASE 0: Day$ = "Saturday"
  23.         CASE 1: Day$ = "Sunday"
  24.         CASE 2: Day$ = "Monday"
  25.         CASE 3: Day$ = "Tuesday"
  26.         CASE 4: Day$ = "Wednesday"
  27.         CASE 5: Day$ = "Thursday"
  28.         CASE 6: Day$ = "Friday"
  29.     END SELECT
  30.     GetDay$ = Day$
  31.  
« Last Edit: August 06, 2019, 10:32:34 am by bplus »

Offline Qwerkey

  • Forum Resident
  • Posts: 755
    • View Profile
Re: FUNCTION and SUB By Reference and By Value
« Reply #5 on: August 06, 2019, 10:40:48 am »
I'd mentioned before that passing via parenthesis wasn't reliable.

Not reliable???  Everything issuing from Microsoft is unreliable, but QB64?  I'm disappointed!

Seriously, the By Value / By Reference is sort-of covered in the Wiki under ByVal (I've just looked) - see image.

The last bullet point under Description says to use parentheses for SUB and FUNCTION calls to pass By Value.  The Errors section below says that you can't use ByVal before STRING or TYPE variables: although slightly different to what I was doing, I suppose that a half-intelligent user might have queried just blundering use of parentheses (brackets).  (I'm still disappointed that a FUNCTION ever changes any of its calling arguments).
« Last Edit: August 06, 2019, 10:49:11 am by Qwerkey »