Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

local Vars in Funktionen



  • Please log in to reply
16 replies to this topic
Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

wie kann ich Autohotkey in Funktionen dazu zwingen dynamische Variablen konsequent als lokale Variabeln zu behandeln

ohne jeden moeglichen Variablennamen einzeln via

local VariablenName 

zu notieren.

Zum Problem habe ich ein Beispielskript vorbereitet:

Loop 10
{
	if(mod(a_index,2))  ; ungerade Ganzahl
	{
		GanzZahl%A_Index%:=A_Index
		AnzeigeGanzzahl:=AnzeigeGanzzahl "	" GanzZahl%A_Index%
	}
}
MsgBox % "Die ungeraden Ganzzahlen bis 10 `n"		AnzeigeGanzzahl 
MsgBox % "8-Fakultaet `n" Fakultaet(8)                 ; dieser Aufruf bewirkt die Aenderungen der Globalen Variablen
Loop 10
{
	if(mod(a_index,2))  ; ungerade GanzZahl
	{
	}
	else		; gerade GanzZahlen
	{
		GanzZahl%A_Index%:=A_Index
		AnzeigeGanzzahl:=AnzeigeGanzzahl " " GanzZahl%A_Index%
	}

}
MsgBox % "Die verutschten GanzZahlen von 1 bis 10`n" GanzZahl1 "	" GanzZahl2 "	" GanzZahl3 "	" GanzZahl4 "	" GanzZahl5 "	" GanzZahl6 "	" GanzZahl7 " 	" GanzZahl8 "	" GanzZahl9 "	 " GanzZahl10 " " 

ExitApp


Fakultaet(IGanzZahl)
{			
	LastErg:=1
	Loop, % IGanzZahl
	{
		LastErg:=LastErg * A_Index
	        GanzZahl%A_Index%:=LastErg
	}
	return GanzZahl%IGanzZahl%
} 

Mit der Funktion Fakultaet() ueberschreibe ich gleichnamige Globale Variablen 

GanzZahl%A_Index%

des Hauptprogramms. Und zwar genau dann, wenn sie im Hauptprogramm schon irgenwo verwendet wurden oder undynamisch auftreten und in der Funktion noch nicht. Ueber

local GanzZahl1, GanzZahl2, GanzZahl3, GanzZahl4, GanzZahl5, GanzZahl6, GanzZahl7, GanzZahl8, GanzZahl9, GanzZahl10,

konnte ich dies verhindern. Dies Methode ist jedoch fuer eine grosse Variablenanzahl nicht mehr brauchbar. Das Anzahl-Element wie bei 

StringSplit,Array,...                       ; Array0

gibt es hier nicht. Ueber dieses koennte ich ja  via 

local GanzZahl0

AHK anweisen alle zugehoerigen Elemente wirklich lokal zu behandeln.

 

Auch die Frage wozu AHK schreibend in Funktionen auf Globale Variabeln zugreift, wuerde mich brennend interessieren.

 

PS. wenn man die Zeile vor ExitApp auskommentiert, sieht man im Debugger schoen wie nur die ungeraden Zahlen ueberschrieben werden.



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

Hallo,
 
was Du da gefunden hast, ist die berüchtigte Oft auftretende Verwechslungsgefahr, im englischen Original zutreffender "Common source of confusion" genannt.

 

Dynamische Variablenreferenzen in Funktionen nutzen zum Zeitpunkt der Ausführung vorhandene globale Variablen, wenn sie nicht ausdrücklich als Local deklariert werden und noch keine lokale Variable existitiert. Eine Anweisung wie MsgBox, %GanzZahl1% innerhalb der Funktion würde z.B. bereits eine lokale Variable GanzZahl1 anlegen, wenn das Skript geladen wird.

 

In Deinem Beispiel werden die ungeraden GanzZahl Variablen in der ersten Loop als globale Variablen erstellt, bevor die Funktion aufgerufen wird. Deshalb werden in der Funktion diese und nur diese globalen Variablen verwendet. Die geraden werden als lokale Variablen erzeugt. SIe sind außerhalb der Funktion nicht sichtbar und werden beim Verlassen der Funktion geleert.

 

Der einfachtse Workaround wäre, in der Funktion auf Variablennamen zu verzichten, die auch global verwendet werden. 


Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

Danke Just Me

 

Der einfachtse Workaround wäre, in der Funktion auf Variablennamen zu verzichten, die auch global verwendet werden. 

Fuer mich war bisher einer der Hauptgruende Funktionen statt Unterprogrammen zu verwenden, um genau dies nicht tun zu muessen.

 

Ich suche nur einen Weg, wie ich AHK sagen kann, dieser Bereich von Variablen ist Lokal, ohne jede einzelne Auspraegung der Variablen im Quelltext der Funktion zu benutzen z.B.:

Dummy:=GanzZahl1, Dummy:=GanzZahl2, Dummy:=GanzZahl3, ...

wuerde funktionieren, aber jede Auspraegung des Variablennamens kam im Quelltext der Funktion vor. Hingegen

Loop, 10
   Dummy:=GanzZahl%A_Index%

reicht nicht, da fuer AHK die rechte Seite ja wieder (nach den selben Bedingungen wie vorher) eine globale Variable sein kann.

 

PS. AutoHotKey 2 scheint mein Skript wie ich es erwartet haette zu interpretieren. Zumindest von den Ergebnissen her gesehen. Ich habe keinen Debugger fuer genauere Tests.



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

Hallo,

 

ja, in v2 soll das endlich geändert werden. Bis dahin gibt es aber noch eine andere Möglichkeit, wenn Du die dynamischen Variablen wie im Beispiel für 'unechte' Arrays verwendest: echte Arrays.


Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

Danke Just Me,

 

schade eigentlich!

 

Da muss ich wohl recherchieren ob die AHK-Version 2 einsetzbar ist (ohne z.B. laufend die Quelltexte nachziehen zu muessen).



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

...
 
schade eigentlich!
 
Da muss ich wohl recherchieren ob die AHK-Version 2 einsetzbar ist (ohne z.B. laufend die Quelltexte nachziehen zu muessen).

 

Warum eigentlich?

 

Wenn wir Dein Beispiel nehmen, braucht es nur zwei zusätzliche Anweisungen, um die Variable jeweils als Array zu deklarieren, die Prozentzeichen   %...% werden durch Klammern ersetzt [...] , und überall da, wo Du keine dynamischen Referenzen verwendest, musst Du den 'Index'-Teil des Variablennamens ebenfalls in Klammern packen:

GanzZahl := []
Loop 10
{
   if(mod(a_index,2))  ; ungerade Ganzahl
   {
      GanzZahl[A_Index]:=A_Index
      AnzeigeGanzzahl:=AnzeigeGanzzahl "   " GanzZahl[A_Index]
   }
}
MsgBox % "Die ungeraden Ganzzahlen bis 10 `n"      AnzeigeGanzzahl
MsgBox % "8-Fakultaet `n" Fakultaet(8)                 ; dieser Aufruf bewirkt die Aenderungen der Globalen Variablen
Loop 10
{
   if(mod(a_index,2))  ; ungerade GanzZahl
   {
   }
   else      ; gerade GanzZahlen
   {
      GanzZahl[A_Index]:=A_Index
      AnzeigeGanzzahl:=AnzeigeGanzzahl " " GanzZahl[A_Index]
   }

}
MsgBox % "Die verutschten GanzZahlen von 1 bis 10`n" GanzZahl[1] "   " GanzZahl[2] "   " GanzZahl[3] "   " GanzZahl[4] "   "
       . GanzZahl[5] "   " GanzZahl[6] "   " GanzZahl[7] "    " GanzZahl[8] "   " GanzZahl[9] "    " GanzZahl[10] " "

ExitApp


Fakultaet(IGanzZahl)
{
   LastErg:=1
   GanzZahl := []
   Loop, % IGanzZahl
   {
      LastErg:=LastErg * A_Index
      GanzZahl[A_Index]:=LastErg
   }
   return GanzZahl[IGanzZahl]
}

Das scheint mir nicht zuviel Aufwand zu sein, und Du bleibst damit auch dann kompatibel, wenn v2 wirklich einmal reif für den produktiven Einsatz wird.


Prefer ahkscript.org for the time being.


just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

Korrektur:
 

... 
In Deinem Beispiel werden die ungeraden GanzZahl Variablen in der ersten Loop als globale Variablen erstellt, bevor die Funktion aufgerufen wird. Deshalb werden in der Funktion diese und nur diese globalen Variablen verwendet. Die geraden werden als lokale Variablen erzeugt. Sie sind außerhalb der Funktion nicht sichtbar und werden beim Verlassen der Funktion geleert.
...


Das ist natürlich Quatsch. Alle 10 Variablen werden durch die Anweisung MsgBox % "Die verutschten GanzZahlen von 1 bis 10`n" GanzZahl1 ... global angelegt, wenn das Skript geladen wird, weil sie in dieser Anweisung eben nicht dynamisch referenziert werden. Die Funktion Fakultaet(8) überschreibt die ersten 8 davon. Das merkt man im Beispiel nur deshalb nicht, weil die geradzahligen Variablen anschließend in der zweiten Loop wieder überschrieben werden, bevor Du die Variablen ausgibst.

 

Na ja, man nennt es nicht ohne Grund "common source of confusion".  ;)


Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013
Danke Just Me,
 
 
ich denke schon, dass Deine Regel mein Problem loest.
allerdings gehe ich von anderen Voraussetzungen aus.
Die Funktion befindet sich in einem groesseren restlichen Gesammtzusammenhang auf den ich ersteinmal keinen Einfluss nehmen moechte. D.h. es kommt nur eine weitere Funktion hinzu. In diesem Falle Fakultaet
 
Fakultaet(IGanzZahl)
{
GanzZahl := []
LastErg:=1i
Loop, % IGanzZahl
{
LastErg:=LastErg * A_Index
       GanzZahl[A_Index]:=LastErgu
}
return GanzZahl[IGanzZahl]
} 
Nun ist klar dass die neue Funktion die globalen GanzZahlen nicht ueberschreibt, da in der Funktion Objekte verwendung finden und ausserhalb nicht. Dieser Zustand loest in den meisten Faellen schon mein Problem, dass ich nur ueber den Rueckgabewert der Funktion globale veraenderungen akzeptiere. Aber selbst wenn ich den Rest auch nach Deiner Regel umforme (also Namensgleichheit habe) werden die globalen Objekte nicht ueberschrieben.
Somit denke ich alles zu haben um in einer Funktion vollstaendig (bis auf den Rueckgabewert) lokal zu bleiben.
 

 

 

Korrektur:
 


Das ist natürlich Quatsch. Alle 10 Variablen werden durch die Anweisung MsgBox % "Die verutschten GanzZahlen von 1 bis 10`n" GanzZahl1 ... global angelegt, wenn das Skript geladen wird, weil sie in dieser Anweisung eben nicht dynamisch referenziert werden. Die Funktion Fakultaet(8) überschreibt die ersten 8 davon. Das merkt man im Beispiel nur deshalb nicht, weil die geradzahligen Variablen anschließend in der zweiten Loop wieder überschrieben werden, bevor Du die Variablen ausgibst.

 

Na ja, man nennt es nicht ohne Grund "common source of confusion".  ;)

wenn man im AusgangsSkript die Zeile vor ExitApp weglaesst,

nur dann werden die ugeraden Werte unterhalb der 8 ueberschrieben,

die geraden sind ja nicht global bekannt.

So wollte ich das Skript ursprunglich beifuegen. 

Man haette dann jedoch das haarstreubende Ergebnis nicht gesehen.

 

 
Wenn ich alles richtig verstanden habe,
lauft jeder der in einer Funktion dynamische Variablen verwendet,
welche nicht explizit undynamisch in eben dieser Funktion vorkommen,
gefahr, dass namensgleiche globale Variablen gezogen werden.
 
Ich denke dass sich hier die Nachprogrammierung einer Direktive der Art
#NoGlobalCopies
oder ein Schalter
NoGlobalCopies[On Of]
dringlich empfehlenswert ist.
Mein Englisch ist zu schlecht, deshalb hier meine Bitte,
ob jemand dies in einem englischen Forum,
welches auch von Lexikos gelesen wird,
posten kann,
sofern dies nicht bereits diskutiert und abgelehnt wurde.

 

nochmals danke

Gerdi



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

 

Wenn ich alles richtig verstanden habe,

lauft jeder der in einer Funktion dynamische Variablen verwendet,
welche nicht explizit undynamisch in eben dieser Funktion vorkommen,
gefahr, dass namensgleiche globale Variablen gezogen werden.
 
Korrekt!
 
Ich glaube nicht, dass das in v1 noch geändert wird. Lexikos ist zwar etwas rigoroser als Chris, aber er wehrt sich dennoch oft beharrlich dagegen, Verhalten so zu ändern, dass alte Skripte nicht mehr laufen. Der Absatz in der Hilfe zeigt ja, dass dieses Problems schon lange bekannt ist. Und gerade wegen dieser langen Zeit wird Lexikos nicht ausschließen wollen, dass jemand dieses 'Feature' in seinen Skripten bewusst dazu nutzt, in einer Funktion globale Variablen zu verändern.

 

 

Und noch etwas:

Im speziellen Fall der Funktion Fakultaet() brauchst Du weder unechte noch echte Arrays:

Fakultaet(IGanzZahl)
{
   Ergebnis := 1
   Loop, % IGanzZahl
   {
      Ergebnis *= A_Index
   }
   return Ergebnis
} 

Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

Verhalten so zu ändern, dass alte Skripte nicht mehr laufen. 

Nach meinem obigen Vorschlag wuerden mit weggelassener Direktive alte Skripte weiterhin laufen.

Mit

NoGlobalCopies[On|Off]                   ; Default Einstellung ist Off 

sogar in gemischten (Alt/Neu) Skripten.

 

Danke,

die Fakultaets-Funktion wurde nur um die Problematik zu verdeutlichen aufgeblaeht.

 

Ich benutze dynamische Variablen gerne um moegliche Ergebnisse vorzuberechnen,

um damit das Skript, waehrend der Benutzer Eingaben macht,  zu beschlaeunigen.

Ich weiss nicht ob bzw. wie oft ich Fehler gesucht und nicht gefunden habe, die obiger Problematik zum Opfer gefallen sind. 

Weil ich ja immer die Ursache in den eigenen Skriptzeilen vermutete.

Dass haeufig in Gleichnamigen globalen Variablen oft auch gleiche, aehnliche oder letzte Staende gespeichert sind macht die Fehlersuche auch mit meinem heutigem Wissen nicht einfacher.  



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

Ok, ich werde das im anderen Forum mal versuchen.

Ich habe dort ja schon oft genug Änderungswünsche geäußert, die bei lexikos auf wenig 'Interesse' gestoßen sind.  ;)


Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

ich wuerde gerne die Ueberschrift dieses Beitrags um

 

(Wissenswertes bei Verwendung dynamischer Variablen in Funktionen.)

 

ergaenzen.

Hat dazu jemand die Berechtigung?



just me
  • Members
  • 1496 posts
  • Last active: Nov 03 2015 04:32 PM
  • Joined: 28 May 2011

Ich glaube, das kann hier nur ein Moderator oder Administrator. Ich weiß auch nicht, wie lang der Titel überhaupt sein darf.

 

Deinen Änderungswunsch habe ich hier eingestellt, allerdings in etwas abgewandelter Form.


Prefer ahkscript.org for the time being.


Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013

 

Deinen Änderungswunsch habe ich hier eingestellt, allerdings in etwas abgewandelter Form.

Spitze, kein neuer Befehl. Es muessten nichteinmal zwangsweise die Hilfedateien nachgezogen werden (eine Erwaehnung in den Changes waere ausreichend)

Ich druecke die Daumen,

 

Gerdi



Gerdi
  • Members
  • 39 posts
  • Last active: Aug 06 2015 11:42 PM
  • Joined: 03 Feb 2013
✓  Best Answer

Zusammenfassung mit dem Versuch alles auf den Punt zu bringen.

 

Jeder laeuft Gefahr, der in einer Funktion dynamische Variablen verwendet,
welche nicht explizit undynamisch in eben dieser Funktion vorkommen,
dass namensgleiche globale Variablen gezogen werden.
Dies gilt fuer lesende als auch fuer schreibende Zugriffe.
Erschwerend kommt hinzu, dass sich dieses Verhalten nicht abschalten laesst.
Ausser durch die bereits erwaehnte undynamische Verwendung jeder Auspraegung der dynamischen Variablen in der Funktion.

A:=20, B:=3
schreibend%A%:=lesend%B%

bei diesem einfachen Skript-Ausschnitt einer Funktion,
wird wenn die Variable

schreibend20

global existiert und lokal nicht,

die globale Variable veraendert.
Ebenso wird die Variable

lesend3

dafuer lesend nach den selben Bedingungen local oder global herangezogen.

Ob dieses Verhalten als Design-Schwaeche oder als Design-Fehler von AutoHotKey gewertet wird,
muss jeder fuer sich entscheiden.
In AutoHotKey Version 2 jedenfalls ist dieses Verhalten nicht mehr.

 

Just Me hat zu dieser Problematik einen Aenderungswunsch noch fuer Version 1.1.x eingestellt. 

 

Umschiffungsmoeglichkeiten:
1. explizite undynamische Verwendung jeder vorkommenden Auspraegung der dynamischen Variablen in der Funktion.
2. Verwendung anderer Variablennnamen in Hauptrogrammm und Funktion (d.h. jeweils alle Auspraegungen muessen sich unterscheiden)
3. Verwendung von Objekten statt dynamischer Variablen

 

Fuer 3. wurde von Just Me ein einfacher Weg aufgezeigt: "... Prozent durch [] ersetzen ..."

(Danke Just Me, mich hat dieser Tipp erheblich bei der Verwendung auch von komplizierteren Objekten weitergebracht.)

 

@Just Me falls Du keine Aenderungswuensche hast, wuerde ich abschliessend diese Antwort als Best Answer markieren.

 

PS. das eingangs gezeigte Skript  habe ich in meiner naechsten Antwort auch versucht auf den Punkt zu bringen.