Return values from function without using global Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
WalkerOfTheDay
Posts: 710
Joined: 24 Mar 2016, 03:01

Return values from function without using global

Post by WalkerOfTheDay » 27 Oct 2022, 08:38

Hi,

Even though I have a few years of experience with AHK, I always seem to struggle to understand Returning values from a function.
Most examples in the guide are quite straightforward, but how do I for instance return the values of SearchX and SearchY
outside my function, without using global ?

Function:

Code: Select all

SearchWeb(SearchTerm, ClickOrNot:="Click")
{
	Global ; TEST 19-10-2022
	WinActivate, ahk_exe firefox.exe			
	WinWaitActive, ahk_exe firefox.exe			
	
	Sleep, 1000							
	Send, ^f
	Sleep, 500
	Send, % SearchTerm
	Sleep, 2000
	PixelSearch, SearchX, SearchY, 0, 0, A_ScreenWidth, A_ScreenHeight, 0x78d838,, Fast
	if ErrorLevel = 0
	{
		Found := "Yes"
		if (ClickOrNot = "Not")
			Sleep, 100
		else
			MouseClick, % "Left", SearchX, SearchY, 1, 10
	}
	else
	{
		MsgBox, % SearchTerm . " niet gevonden!"
		Found := ""
	}
	Return
}
Label:

Code: Select all

Print_Label:
GuiControl, disable, Print label(s)
SearchWeb("Status", "Not") 
SearchX := SearchX+2               ;<<<<<<<<<<<<<<<<<<<<<< 
SearchY := SearchY+82             ;<<<<<<<<<<<<<<<<<<<<<<
Sleep, 50
Click, %SearchX%, %SearchY%			; checkbox for status
SearchWeb("Print Verzendlabel(s)")
Return

RussF
Posts: 1311
Joined: 05 Aug 2021, 06:36

Re: Return values from function without using global

Post by RussF » 27 Oct 2022, 09:43

Functions can only return one item, and that is done by referencing that item in a return statement (I use the word "item" rather than "value" for a reason). For example:

Code: Select all

MySum := MyAdd( 2, 3)
MsgBox, % My sum is " . MySum
ExitApp

MyAdd(n1, n2) {
	result := n1 + n2
	Return result
}
Because of the beauty of expressions, that can be shortened to:

Code: Select all

MsgBox, % My sum is " . MyAdd( 2, 3)
ExitApp

MyAdd(n1, n2) {
	Return n1 + n2
}
I used the word item, because that "item" could be an array with multiple values. For example, here I use an associative array with keys to return coordinates:

Code: Select all

XYArray := MyCoords()
MsgBox, % "My X is " .  XYArray["MyX"] . " and my Y is " . XYArray["MyY"]
ExitApp

MyCoords() {
  MyCoordArray := {"MyX": 0, "MyY": 0}	; create the array with initial values of 0
  ; process code here to get coord values
  MyCoordArray["MyX"] := 25
  MyCoordArray["MyY"] := 100
  Return MyCoordArray
}
When you have a function that returns a value(s), you can use assign the value(s) of that function to a variable as in my first example, or you can use that function directly in an expression as in my second.

A function that you create is no different than the functions built into AHK. For example,

Code: Select all

MyVar := Trim("   abcde   ")
assigns the value returned by Trim(), which in this case is "abcde" to the variable MyVar. The function itself has a value and that value is whatever is returned by the function's Return statement.

You cannot, however, just call a function and have it assign values (as you did in your example) without using global variables. [Edit] Except when using ByRef - see below.

Hope this helps.

Russ
Last edited by RussF on 27 Oct 2022, 11:31, edited 1 time in total.

User avatar
flyingDman
Posts: 2846
Joined: 29 Sep 2013, 19:01

Re: Return values from function without using global

Post by flyingDman » 27 Oct 2022, 10:48

https://www.autohotkey.com/docs/Functions.htm#ByRef wrote:Since return can send back only one value to a function's caller, ByRef can be used to send back extra results. This is achieved by having the caller pass in a variable (usually empty) in which the function stores a value.
14.3 & 1.3.7

RussF
Posts: 1311
Joined: 05 Aug 2021, 06:36

Re: Return values from function without using global  Topic is solved

Post by RussF » 27 Oct 2022, 11:24

@flyingDman, you are correct. I omitted that because it is a concept that can be a bit more difficult to understand than passing values. It is also usually used in cases where you are working with a large array in its entirety where you don't want to duplicate the array's structure within the function. Using ByRef in my example above would look like this:

Code: Select all

MyX := 0
MyY := 0
MyCoords(MyX, MyY)
MsgBox, % "My X is " .  MyX . " and my Y is " . MyY
ExitApp

MyCoords(ByRef ThisX, ByRef ThisY) {
  ; process code here to get coord values
  ThisX := 25
  ThisY := 100
  Return
}
Russ

User avatar
WalkerOfTheDay
Posts: 710
Joined: 24 Mar 2016, 03:01

Re: Return values from function without using global

Post by WalkerOfTheDay » 28 Oct 2022, 02:31

Thanks to you both for the examples.

So in the function I posted, keeping it global would be the prefered way ?

Lepes
Posts: 141
Joined: 06 May 2021, 07:32
Location: Spain

Re: Return values from function without using global

Post by Lepes » 28 Oct 2022, 04:05

To me, the best programming practice is using "byRef", because you know the function needs two parameters that are modified inside the function, and when you are calling the function you even know those parameters names.

If a function returns an array or associated array, the name of the function should be descriptive enough, so you don't need to read the "function help" to understand what is returning. In this case I prefer an array, because associated arrays needs parameters names and you could make a typo, something very difficult to discover when you debug.

Also try to do your owns functions following AHK commands, I mean, The same way AutoHotkey does, that way is more intuitive to use.

If I open an AHK file and I see "global" my stomach get up side down, you don't really know where the global variables are used and changed. This is a nightmare to follow the code, modify the code and debug. You can do it if the script is very simple, but in my opinion, any script get complex at the end, when you are constantly modifying it.

Code: Select all

 
FindTermOnWebAtXY(SearchTerm, ClickOnIt:=1, ByRef X:=0, ByRef Y:=0)
{

	; Global ; TEST 19-10-2022
	WinActivate, ahk_exe firefox.exe			
	WinWaitActive, ahk_exe firefox.exe			
	
	Sleep, 1000							
	Send, ^f
	Sleep, 500
	Send, % SearchTerm
	Sleep, 2000
	PixelSearch, X, Y, 0, 0, A_ScreenWidth, A_ScreenHeight, 0x78d838,, Fast
	if ErrorLevel = 0
	{
		Found := 1   ; Return true if I found the pixels search
		if (ClickOnIt )   ; more readable
			MouseClick, % "Left", X, Y, 1, 10
		else
			Sleep, 100
	}
	else
	{
		; MsgBox, % SearchTerm . " niet gevonden!"  ; not a good idea to hardcode a Msgbox inside a function, do it outside of the function
		Found := 0   ; Return False if i didn't find
	}
	Return  Found
}

if FindTermOnWebAtXY("Status", "Not", SearchX, SearchY) 
{
	SearchX := SearchX+2               ;<<<<<<<<<<<<<<<<<<<<<< 
	SearchY := SearchY+82  
}
else {
   ; didn't find  pixelSearch
   MsgBox, % SearchTerm . " niet gevonden!"  ; the user of the function do a Msgbox if he wants!
 }
The name of the function is very important too:
- "SearchWeb" tells you there are infinite results as expected if you do a google search.
- "FindTermOnWebAtXY" tells you that you can find the results or not, a boolean value you can use.

This is called legibility of the code, it's easy human readable " if FindTermOnWebAtXY" than "if SearchWeb".

I always think the name of the function once I have coded it, because I don't know what exactly a function should do before writing the function. Once coded, I change the name of the function to something more descriptible

Edit: I miss the "clickOrNot" parameter name. You should always use positive values and verbs... I mean, instead of ClickOrNot:= "string here With UPcase and Lowercase" try to call it ClickOnIt := 1 (True, it must be clicked) or ClickOnIt := 0 (False, should NOT be clicked)

RussF
Posts: 1311
Joined: 05 Aug 2021, 06:36

Re: Return values from function without using global

Post by RussF » 28 Oct 2022, 08:11

WalkerOfTheDay wrote: So in the function I posted, keeping it global would be the prefered way ?
The term "preferred" is subjective. @Lepes makes some valid points about trying to minimize the use of global variables. Ideally, a function should be a "black box" that has a defined set of input arguments, does a specific job and returns a result. Let's say you have a large function that takes 2 arguments and is called from several places in your code. Suddenly you realize that in 1 or 2 specific cases, you need to pass it 3 arguments, but the rest of the time, 2 will suffice. Rather than duplicate the entire function, you could just add that third argument with a default value at the end of the argument list. It then becomes an optional argument. For example:

Code: Select all

MyFunction(Arg1, Arg2)
; becomes
MyFunction(Arg1, Arg2, Arg3 := 0)
; or
MyFunction(Arg1, Arg2, ByRef Arg3 := 0)
You then would not have to duplicate the function for a specific use case, nor would you have to find all the places it is called from within your code to add that third argument. It is optional.

That said, in order to use ByRef arguments, you must have pre-defined those variables prior to calling the function. If you are calling that function from within another function, then those variables would be local to function you are calling from and is the preferred method.

However, if it is a small script that you don't anticipate growing, and the function is called from the autoexec section of your code, those ByRef variables would still need to be defined, but they would then be global by default. So whether you add those variables as ByRef arguments to the function or just place Global at the top of the function, makes no technical difference.

Most experienced coders will tell you to pass the arguments anyway, because not only is it a good programming habit to get into, but should you find later on that you really need to expand that script, or that you can use that function in another, larger, piece of code, then it is virtually ready with little to no modifications.

Have I made things clearer or muddier?

Russ

User avatar
WalkerOfTheDay
Posts: 710
Joined: 24 Mar 2016, 03:01

Re: Return values from function without using global

Post by WalkerOfTheDay » 31 Oct 2022, 03:04

Thanks for the extensive information guys ! I love this community. What a difference to (some) of the Linux community :D

I will study your examples and try to understand them :)

Post Reply

Return to “Ask for Help (v1)”