How to pass A_Index to => Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

How to pass A_Index to =>

27 Oct 2021, 13:42

I want to set up Hotkey in batches
No need to go line by line
Is there any solution?

Code: Select all

key1 := "a"
action1 := "1"
key2 := "b"
action2 := "2"
key3 := "c"
action3 := "3"
key4 := "d"
action4 := "4"
loop
{
    if !IsSet(key%A_Index%) ; key5 Variable not found.
        Break
;    if key%A_Index% && action%A_Index%
;        Hotkey(key%A_Index%, (*) => (Action(action%A_Index%))) ;action0 Variable not found.

    i := A_Index
    if key%i% && action%i%
        Hotkey(key%i%, (*) => (Action(action%i%)))  ;all action%i% is 4
}

;line by line and work fine
;if key1 && action1
;    Hotkey(key1, (*) => Action(action1))
;if key2 && action2
;    Hotkey(key2, (*) => Action(action2))
;if key3 && action3
;    Hotkey(key3, (*) => Action(action3))
;if key4 && action4
;    Hotkey(key4, (*) => Action(action4))

Action(item)
{
    MsgBox item
}
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: How to pass A_Index to =>

28 Oct 2021, 14:29

closures capture variable references, not values. what uve written amounts to "at the time the closure is invoked, look for a variable called action concatenated with whatever the variable i evaluated to, fetch its value and call Action() with that value". if at the time of the invocation, either i cant be looked up(eg because its not in the local/global/upvar scope), or action cant be concatenated with i(eg because of type mismatches) or action concatenated with i cant be looked up(eg because its not in the local/global/upvar scope), ud get an error... which is exactly what u got.
  • make a helper function, to which ull pass the value u want to bind, that then returns a closure referencing that helper function's parameter(which is where u passed the value u wanted to bind in)
  • or keep the closure as is, but declare an additional parameter to which ull then .Bind() the desired value. the closure's body then reference that parameter, not outside variables(that may or may not exist anymore)
lexikos
Posts: 9551
Joined: 30 Sep 2013, 04:07
Contact:

Re: How to pass A_Index to =>

29 Oct 2021, 04:44

Why are you trying to use dynamic variables for this?

There are several obvious alternatives which are cleaner and less error-prone. If you use an Array (e.g. [{key: "a", action: "1"}, ...]), it automatically has a defined length that could be used to limit Loop. If you use an Array or Map, a for loop can give you all of the items without requiring you to check when you've reached the end. Even defining hotkeys one to a line in a continuation section, or in an INI file would work; Loop Parse can give you the lines that exist, without requiring you to terminate the loop explicitly.

None of these methods would require creating two (global) variables for each hotkey or explicitly numbering each hotkey. What if you want to remove or comment out a hotkey in the middle of the list?
swagfag wrote:if at the time of the invocation, [...] ud get an error... which is exactly what u got.
You're talking about errors that might be raised by the fat arrow function, but that is not what is going on at all. This script produces an error at the dynamic variable reference key%A_Index% outside the closure. Once that is fixed, it does not produce an error.

This is correct, and close to the problem:
closures capture variable references, not values.
(*) => (Action(action%i%)) works similarly to ((&i, *) => (Action(action%i%))).Bind(&i).

But it is of only very limited relevance, because there are no closures in this script.

viv wrote:; key5 Variable not found.
If you must, use try/catch.
viv wrote:;all action%i% is 4
If all of this code was within a function, the variable i (not its current value) would be captured by the closure. In this case there is no closure, just a fat arrow function referencing a global variable. Either way, by the time the closure is called, i has the value that it was last assigned (4).

The two solutions suggested by swagfag are correct. Both can be done with => or a full function.
((i, *) => Action(action%i%)).Bind(i)
((i) => (*) => Action(action%i%))(i)

But this is not efficient. If you need to account for action%i% changing at runtime, resolve it to a VarRef (only once, when the hotkey is defined, not every time it is activated):
((&v, *) => Action(v)).Bind(&action%i%)

If the action value never changes at runtime, resolve it to a value.
((a, *) => Action(a)).Bind(action%i%)
((a) => (*) => Action(a))(action%i%)

But I would still recommend learning to avoid dynamic variables.
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

Re: How to pass A_Index to =>

29 Oct 2021, 05:06

@lexikos

The whole looks like this
GUI requires default values
INI saves and reads default values

In the write section
I can pass 2 values
So a single Loop will solve

But in the keybind section
because it calls a function that accepts only one value (ThisHotkey)
So I don't know how to pass action1234 in
I can only solve it line by line

Only after posting it in full did I realise that this &action was unnecessary
There is an AltSubmit parameter

Code: Select all

loop 4
    GuiHotkey("key" A_Index, "action" A_Index, set.key%A_Index%, set.action%A_Index%)

GuiHotkey(key, action, keyDefault := "", actionDefault := 1, opt := "xs")
{
    ; Action(item)
    actionArr := [lang.actionPrev, lang.actionNext, lang.actionLocation, lang.actionDelete, lang.trayList, lang.trayRandom, lang.trayReIndex, lang.traySetting, lang.trayReload, lang.trayExit]
    main.Add("Hotkey", opt " w160 v" key, keyDefault)
    main.Add("text", "yp", "   >>>   ")
    main.Add("DDL", "yp AltSubmit Choose" actionDefault " v" action, ActionArr)
}

;write
v := main.Submit(0)
Loop 4
{
    IniWrite(v.key%A_Index%, ini, "Other", "key" A_Index)
    IniWrite(v.action%A_Index%, ini, "Other", "action" A_Index)
}

;keybind
if set.key1 && set.action1
    Hotkey(set.key1, (*) => Action(set.action1))
if set.key2 && set.action2
    Hotkey(set.key2, (*) => Action(set.action2))
if set.key3 && set.action3
    Hotkey(set.key3, (*) => Action(set.action3))
if set.key4 && set.action4
    Hotkey(set.key4, (*) => Action(set.action4))
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: How to pass A_Index to =>  Topic is solved

29 Oct 2021, 06:10

Code: Select all

; hotkey array
Hotkeys := []
; fill the array with items (e.g. read from INI)
Hotkeys.Push({Key: "a", Action: 1})
HotKeys.Push({Key: "b", Action: 2})
HotKeys.Push({Key: "c", Action: 3})
; process the array
For HK In Hotkeys {
   HotKey(HK.Key, Action.Bind(HK.Action))
}

Action(Value, HK) {
   MsgBox(Value)
}
?
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

Re: How to pass A_Index to =>

29 Oct 2021, 06:30

@just me

Thank you very much
After reading it, I realized that the main reason is that the parameters are dynamic

I've also been thinking of relying on a variable(i) to point to it
Using multiple variables(array[map1,map2]) to link them together would be the exact point

This is the final approach in my problem
Thank you again

Code: Select all

Hotkeys := []
loop 7
    if set.key%A_Index% && set.action%A_Index%
        Hotkeys.Push({Key: set.key%A_Index%, Action: set.action%A_Index%})

for HK in Hotkeys
    HotKey(HK.Key, Action.Bind(HK.Action))
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: How to pass A_Index to =>

29 Oct 2021, 06:42

Code: Select all

    if set.key%A_Index% && set.action%A_Index%
How do you construct the Set object?
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

Re: How to pass A_Index to =>

29 Oct 2021, 06:49

@just me
it is IniRead batch

Code: Select all

ini := "Config\config.ini"
set := Map()
Loop Read ini
    If InStr(A_LoopReadLine, "=")
        set.%RegExReplace(A_LoopReadLine, "(.+?)( *)?=.*", "$1")% := RegExReplace(A_LoopReadLine, ".+?=( *)?(.*)", "$2")
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: How to pass A_Index to =>

29 Oct 2021, 07:11

Here's a slight tangent, but still related to fat arrow functions ...

In the case of .DefineProp() and defining Get and Set:

Code: Select all

obj := {}
obj._prop := ""
obj.DefineProp("test",{Set:(a,val) => a._prop := val
                     , Get:(a) => a._prop}) ; a is the "parent object", aka obj, implicitly passed as 1st param

obj.test := "string"
msgbox obj.test " / " obj._prop ; returns "string / string"

I found this difficult to wrap my head around at first. But it is a very handy use of fat arrow funcs.
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

Re: How to pass A_Index to =>

29 Oct 2021, 07:31

@TheArkive

It's too difficult for me...
I don't even know how it relates to this topic...
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: How to pass A_Index to =>

29 Oct 2021, 07:42

@viv
True, it is a bit of a tangent. I've seen several people struggle with using fat arrow funcs (myself included), and your question started off using a fat arrow func.

Sorry if that didn't help. Wasn't trying to confuse.
viv
Posts: 217
Joined: 09 Dec 2020, 17:48

Re: How to pass A_Index to =>

29 Oct 2021, 08:20

@TheArkive

I'm not complaining.
I am grateful to anyone for their help and of course to you.

But it's just the surface meaning, and often the answers are too complicated for me...

And as a non-English speaker, I communicate through online translators
I'm sorry that I may have given the person a bad feeling sometimes
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: How to pass A_Index to =>

29 Oct 2021, 08:23

@viv
It's ok :)

No bad feelings here. I tried to help, and failed.

That's just a fact. :P

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: a_bolog, Auntiejack56, lexikos and 21 guests