Combining variables and when to use %

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
WeThotUWasAToad
Posts: 312
Joined: 19 Nov 2013, 08:44

Combining variables and when to use %

17 Sep 2017, 21:02

Hello,

How do you know:
[1] when to enclose a variable between two %'s?
[2] when a variable should/can be used directly in a command (vs as part of an expression defining a different variable)?


I use variables quite a bit but it seems like I'm commonly not sure exactly how to insert them into a script.

For example, I've currently got a long list of phrases in a Word document and I'm trying to create a script which will assign the correct hyperlink to each phrase from a list of (previously created) bookmarks. The goal is to create a Table of Contents of sorts with each phrase linked to a specific location in the document.

The list of phrases in the document and the list of bookmarks — as it appears in the Insert Hyperlink box (Ctrl+k) — are in a matched order. In other words, after correctly assigning a particular bookmark to it's corresponding phrase, the script simply needs to select the next phrase and apply the next bookmark, then select the next phrase and apply the next bookmark, and then continue that cycle/loop.

The first part (selecting each phrase in the list in sequence) is not a problem but the second part is where I'm struggling. When I go through the process manually (once the Insert Hyperlink box is opened), any bookmark in the list can be selected by some number of {PgDn} presses combined with some number of {Down} presses.

So I understand (I think) all the needed steps but knowing how to insert the variables correctly is what's eating my lunch.

For example, suppose you got to the previous (most recent bookmark to be assigned) by opening the Insert Hyperlink box then pressing {PgDn} three times followed by {Down} five times. If those two values are represented by the variables:

pgs:=3
prev:=5

then how should they be inserted — along with the built-in variable A_Index — to obtain the desired result?

Here is the script I've written so far:

Code: Select all

F1::
; sleeps
	short:= 200
	medium:=600
	long:=1200
; other variables
	pgs := 3				; number of times to press {PgDn}
	prev := 5				; number of times to press {Down}
	rowht := 29				; height (in pixels) of each row in the document
Loop, 10
{
CoordMode, Mouse, Screen
MouseGetPos px,py			; captures beginning mouse position

Click
    Sleep, short
Send {Shift Down}
Send {End}					; selects phrase
Send {Shift Up}
    Sleep, short

Send ^k						; opens Insert Hyperlink box
    Sleep, medium

Send {PgDn pgs}
    Sleep, medium
Send {Down (prev+A_Index)}	; supposed to define the list position of the next bookmark
    Sleep, medium
Send {Enter}

MouseMove %px%,%py%
    Sleep, medium
MouseMove, 0, %rowht%,, R	; moves down exactly one row for the next loop
    Sleep, medium
}
Return
I also tried this variation but it does not work either:

Code: Select all

...
Send {PgDn pgs}
    Sleep, medium
var:=prev+A_Index
Send {Down var}				; supposed to define the list position of the next bookmark
    Sleep, medium
Send {Enter}
...
So in addition to needing help with this script, I'd appreciate any suggestions or guidelines regarding inserting variables. For example, this script includes:

Sleep variables
  • short:= 200
    medium:=600
    long:=1200
which I know from experience do not require %'s when inserted:
  • Sleep, short
    Sleep, medium
Position variables
  • MouseGetPos px,py
which I know from experience do require %'s
  • MouseMove %px%,%py%
Other variables
  • pgs := 3
    prev := 5
    rowht := 29
which I'm not sure how to insert
  • Send {PgDn pgs}
    Send {Down (prev+A_Index)}

    MouseMove, 0, %rowht%,, R
And combined variables (to create a new variable)
  • var:=prev+A_Index
    Send {Down var}
Thanks
A ------------------------------ [A LOT OF SPACE] ------------------------------ LOT

"ALOT" is not a word. It never has been a word and it never will be a word.
"A LOT" is 2 words. Remember it as though there's [A LOT OF SPACE] between them.
User avatar
Spawnova
Posts: 554
Joined: 08 Jul 2015, 00:12
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 02:23

Generally normal functions never need percents functionName(param1,param2), it's a bit different with built in ahk functions that use comma to separate parameters, some do need percent signs some don't, however a lot of them do work without percent signs including MouseMove.

Code: Select all

f2::
loop 25 { ;simple loop to move mouse from 0,0 to width/height
	x := (a_screenwidth / 25) * a_index
	y := (a_screenheight / 25) * a_index
	MouseMove,x,y
}
return
I'm not sure if you are able to do any sort of math inside send brackets, normally I would make a temp variable to hold the value then send that

Code: Select all

;Send {Down (prev+A_Index)}
tempVar := prev+A_index ;5+1
Send {down %tempVar%} ;need percent signs here when using variables
You basically figured it out on your own, you just needed to add percent signs on the new variable =P
Noesis
Posts: 301
Joined: 26 Apr 2014, 07:57

Re: Combining variables and when to use %

18 Sep 2017, 03:14

Yeah, It can be kind of confusing,

The rule of thumb I use is if the command isn't expecting a variable as an argument you'll need to use %'s. I generally prefer to Force an Expression (percent followed by space),
MouseMove, % px, % py ; instead of enclosing i.e. %var%
as it also allows proper expression syntax to be used with the variable instead of just allowing a single variable. So things like objects/arrays act just like variables and, can be used along with math operations which won't work with the enclosing method. The thing to remember with this method is a comma (i.e. new argument), will usually end it, and so needs to be done for each argument you want to use expressions with.

Personally I still use percents with sleep, even though you don't HAVE to, it also doesn't hurt to do so, it just means that the rule of thumb can be followed, the code will be clearer, and also consistent.

Anyway I digress, the real issue with what you posted is the Send command, and that command doesn't support "Forcing an Expression" and requires the variable substitution by enclosing %'s. One thing to remember with send is it will substitute the string first and then execute the send as it would appear. so

Code: Select all

pgs := 3
string := "{PageDn " . pgs . "}" ; or the same simply using a space instead of periods for auto-concatenation.
Send, string		; would send the actual word "string" as it's considered literal
Send, {string}		; string is literal and is not a keyword for {} type options, note: if you used a single letter var like x it would send a literal x
Send, {PageDn pgs}	; while PageDn is a keyword, it's still literal and, pgs is also literal and isn't a keyword. (may still produce a single page down key press as a side effect however)

Send, {PageDn %pgs%}	; would work as the var pgs contains a keyword (I'm using keyword generally so a number isn't any different to a word in this context)
Send, %string% 			; would send the contents of string, and in this case the contents is identical to the send directly above, point of this is {} chars can be included in the string to send and work as expected if desired
Another thing to consider with using % or not is if you are actually calling a command or a function. Functions always accept variables, while commands may but may not depending on the command being used. The only way to know for sure is to check the command documentation.

Anyway looking at you're specific examples,

With positions, MouseGetPos & MouseMove are actually different, as in, the get doesn't require %'s but the move does, however, you seem to know this, and it's another general rule that if the command is getting something then there is going to be one or more variable arguments in there somewhere, but if the command is doing something, then it's likely going to need %'s.
which I'm not sure how to insert

Send {PgDn pgs}
Send {Down (prev+A_Index)}

MouseMove, 0, %rowht%,, R
First one is covered, the second one, won't work, no you can't use brackets like that with send (you can in some other commands however). You'd have to use a single variable there, but enclosed in %'s, the last one, should be fine. Is there an issue with it?, it could also be done this way:
MouseMove, %px%, % py+rowht
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
Location: Denmark
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 03:32

I force expressions as well.
Noesis wrote:Anyway I digress, the real issue with what you posted is the Send command, and that command doesn't support "Forcing an Expression" and requires the variable substitution by enclosing %'s. One thing to remember with send is it will substitute the string first and then execute the send as it would appear.
You can still use "Forcing an Expression" with Send the problem is that it expects a string.

Code: Select all

Send, % "{PageDn " pgs "}"
The same is true of Gui options and some other commands.

One thing to note is that if you enclose a variable in % in an expression you will get the value of the variable with the name the first variable contains.

Code: Select all

v := "I am v"
x := "v"
MsgBox, % %x%
Please excuse my spelling I am dyslexic.
Noesis
Posts: 301
Joined: 26 Apr 2014, 07:57

Re: Combining variables and when to use %

18 Sep 2017, 03:54

Ok thanks Capn, I probably stuffed up when I last tested forcing expressions with send, which was several years ago when I was a noob at ahk and, have just been coding around it since, good to know.
User avatar
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
Location: Denmark
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 04:00

Noesis wrote:Ok thanks Capn, I probably stuffed up when I last tested forcing expressions with send, which was several years ago when I was a noob at ahk and, have just been coding around it since, good to know.
I should stop clarifying things first thing in the morning, it should have read "the problem is that it expects a string with a specific format."
Please excuse my spelling I am dyslexic.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Combining variables and when to use %

18 Sep 2017, 04:06

I generally approach commands/functions like this:

Code: Select all

Func(VarByRef, Var, "String", Num, Num+Num, %VarName%)
Cmd, VarByRef, % Var, % "String", Num, % Num+Num, % %VarName% ;preferred
Cmd, VarByRef, %Var%, String, Num, % Num+Num, % %VarName% ;also
Oftentimes parameters for commands are 'smart', so you can do things that ordinarily you would not be able to do, but force expression is the simple approach that is also most likely to work.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: Combining variables and when to use %

18 Sep 2017, 05:57

How do you know:
[1] when to enclose a variable between two %'s?
When you want to pass the value of a variable to a command that takes things literally
command , something, something
like
send, {down}
vs
StringToSend:="{down}"
send, %StringToSend%

if send was a function() it would not need the %%,
send("{down}")
or
StringToSend:="{down}"
send(StringToSend)

[2] when a variable should/can be used directly in a command (vs as part of an expression defining a different variable)?
I use an interim variable, I stick to the double %% and avoid the single %
var1:="{up}"
var2:="{left}"
var3 = var1 . var2
send, %var3%

in the case of an object variable or function...
t := this.Varname
send, %t%
t := this.Func()
send, %t%

That's it. I live by those 3 simple rules. %variablename% for commands, variablename for functions, %tempvariable% for holding the result of an expression and pass it to a command
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 07:06

jeeswg wrote:I generally approach commands/functions like this:

Code: Select all

Func(VarByRef, Var, "String", Num, Num+Num, %VarName%)
Cmd, VarByRef, % Var, % "String", Num, % Num+Num, % %VarName% ;preferred
Cmd, VarByRef, %Var%, String, Num, % Num+Num, % %VarName% ;also
Regarding, % %VarName%, this is not safe in general.
%var% wrote:For backward compatibility, command parameters that are documented as "can be an expression" treat an isolated name in percent signs (e.g. %Var%, but not Array%i%) as though the percent signs are absent. This can be avoided by enclosing the reference in parentheses; e.g. Sleep (%Var%).
Also, note from the above quote: can be an expression. When using a command, you should look for this in the documentation. For example, the x and y parameters of mouseMove can be expressions. That is, mouseMove, x,y do not require any %, and can even be mouseMove, x*2, y+10 and such.
Cheers.
OT
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Combining variables and when to use %

18 Sep 2017, 13:54

@Helgef: % %VarName% should always be safe, because you force an expression. Those warnings apply when you *don't* force an expression.

I use parameters in commands, in a consistent way, that will make it easier to automatically convert my current scripts to AHK v2. However, realistically if you aren't consistent, it won't be too much extra work to fix your scripts.

@icuurd12b42: I would do this:

Code: Select all

Send, % StringToSend
Send, % var1 . var2
Send, % var1 var2 ;I usually miss out the .
Send, % this.Func()
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 14:26

jeeswg wrote:Those warnings apply when you *don't* force an expression.
How did you reach that conclusion? It is incorrect.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Combining variables and when to use %

18 Sep 2017, 14:34

This is how I would use % %VarName%:

Code: Select all

q::
MyVar := "hello"
VarName := "MyVar"
MsgBox, % %VarName%
return
You can use that syntax wherever force expression is accepted, which is almost everywhere.

For the record, I would hardly ever have the need to do this.

==================================================

[EDIT:] @Helgef: I searched the documentation for 'can be an expression', it seems to be generally for parameters that expect a number. I did some tests, and I have retrieved some results that are surprising, and which I have marked as such. This behaviour appears bug-like, if you can provide some evidence from the documentation that states that this behaviour is to be expected, that would be most welcome. Cheers.

Code: Select all

w::
vText := "abcdefghijklmno"
5 := 10
Count := 5
Array3 := 15
i := 3
j := "k"
k := 1

MsgBox, % "batch 1"
StringLeft, vOutput, vText, Count ;abcde
MsgBox, % vOutput
StringLeft, vOutput, vText, %Count% ;abcde
MsgBox, % vOutput
StringLeft, vOutput, vText, (Count) ;abcde
MsgBox, % vOutput
StringLeft, vOutput, vText, (%Count%) ;abcdefghij
MsgBox, % vOutput
StringLeft, vOutput, vText, Array%i% ;abcdefghijklmno
MsgBox, % vOutput

MsgBox, % "batch 2"
var := %Count% ;10
MsgBox, % var
var := %j% ;1
MsgBox, % var

MsgBox, % "batch 3"
StringLeft, vOutput, vText, % Count ;abcde
MsgBox, % vOutput
StringLeft, vOutput, vText, % %Count% ;abcde ;SURPRISING: expected abcdefghij
MsgBox, % vOutput
StringLeft, vOutput, vText, % %j% ;(blank) ;SURPRISING: expected a
MsgBox, % vOutput
StringLeft, vOutput, vText, % k ;a
MsgBox, % vOutput
StringLeft, vOutput, vText, % (Count) ;abcde
MsgBox, % vOutput
StringLeft, vOutput, vText, % (%Count%) ;abcdefghij
MsgBox, % vOutput
StringLeft, vOutput, vText, % Array%i% ;abcdefghijklmno
MsgBox, % vOutput
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 16:23

jeeswg wrote:This is how I would use % %VarName%:
That is ok, but it is not really relevant.
jeeswg wrote:This behaviour appears bug-like, if you can provide some evidence from the documentation that states that this behaviour is to be expected, that would be most welcome.
Sure,
%var% wrote:For backward compatibility, command parameters that are documented as "can be an expression" treat an isolated name in percent signs (e.g. %Var%, but not Array%i%) as though the percent signs are absent. This can be avoided by enclosing the reference in parentheses; e.g. Sleep (%Var%).
Cheers :)
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 16:42

@jeeswg see also the documentation for return:
Known limitation: For backward compatibility and ease-of-use, the following two examples are functionally identical:

return MyVar
return %MyVar%

In other words, a single variable enclosed in percent signs is treated as a non-expression. To work around this, make it unambiguously an expression by enclosing it in parentheses; for example: return (%MyVar%).
source: return

Code: Select all

funcF(__v) {
return % %__v%
}
deref(__v) {
return (%__v%)
}
subVar := "test"
var := "subVar"
MsgBox % funcF(var)
MsgBox % deref(var)
my scripts
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Combining variables and when to use %

18 Sep 2017, 17:14

@A_AhkUser: Cheers, great response A_AhkUser. I have worked out some examples below.

@Helgef: Haha, yes, I thought you might repeat that quote. You understand what I mean don't you. Clearly to me I expected that '% ...', i.e. force expression, means safety, that as soon as you start a parameter in a command with '% ', it's going to work just like it does in a function, you literally 'force expression'. But that doesn't always seem to be the case, it emerges now.

My MsgBox example was relevant because it's a force expression, with a variable in percentage signs, and it's working like I expected.

If people don't come up with some good evidence re. what I've stated, (i.e. that it's mentioned somewhere in the documentation,) this is going to straight to Bug Reports. There is the principle of least astonishment, and I am astonished by this behaviour.

Some tests with 'return'.

Code: Select all

q::
MsgBox, % TestForceXpn1() ;b ;expected c
MsgBox, % TestForceXpn2() ;c

d := "e"
e := "f"
TestForceXpn3(%d%) ;f
TestForceXpn3((%d%)) ;f
return

TestForceXpn1()
{
	a := "b"
	b := "c"
	return % %a%
}
TestForceXpn2()
{
	a := "b"
	b := "c"
	return % (%a%)
}

TestForceXpn3(arg)
{
	MsgBox, % arg
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Combining variables and when to use %

18 Sep 2017, 17:38

@Helgef: Haha, yes, I thought you might repeat that quote
:lol:
You understand what I mean don't you.
I do understand you, but it is you that need to produce documentation stating that forcing an expression for a command parameter which already can be an expression changes the documented behaviour quoted (twice) by me, and then (similarly) by A_AhkUser.
My MsgBox example was relevant
The key point here is the odd (but docummented) behaviour when a command paramter can be an expression, the text paramter of msgbox isn't one of those.

Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Combining variables and when to use %

18 Sep 2017, 17:54

The way I see that documented behaviour is that it talks about %var% / (%var%) but says nothing of % %var% / % (%var%).

I only found one reference to 'force an expression':
Variables and Expressions
https://autohotkey.com/docs/Variables.htm
Force an expression: An expression can be used in a parameter that does not directly support it (except OutputVar parameters) by preceding the expression with a percent sign and a space or tab. In [v1.1.21+], this prefix can be used in the InputVar parameters of all commands except the traditional IF commands (use If (expression) instead). This technique is often used to access arrays.
It mentions OutputVar parameters as an exception, which makes sense, because those would be ByRef variables in a function (think SplitPath and WinGetPos). From what is stated there is no implication or expectation that force expression would work differently for certain arguments of certain commands.

Regarding which parameters accept force expression (whether they work in the manner I expect them to or not). My experience has been essentially that force expression has worked everywhere I wanted it to, except for the Loop control flow statement, but a recent update even made that possible.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Combining variables and when to use %

19 Sep 2017, 05:10

It doesn't say that forced expressions are going to change the way expressions are handled, and expressions which consist of an isolated variable enclosed in %-signs in a command paramter which can be an expression will be interpreted as if the %-signs were absent. If you think the docs are unclear, suggest improvement. I do not know enough about older AHK versions to determine if there would be any issue with backward compability if you wish to change the behaviour to match your (by now previous) expectations. If you still think this is a bug you should make a report. In any case, this discussion has, in my opinion, slightly derailed and should continue wherever you feel if belongs.

Cheers.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Billykid, dinodragon, mcd and 146 guests