autohotkey2.0勉強中(翻訳)その8-2~関数編その2~
はじめに
翻訳とかあんま得意じゃないんだけど、関数編その2
その1はこちら
間違いあったらコメくれれば訂正します。というか自分用だし、間違えて覚えてしまうから(´・ω・`)
参考:公式ヘルプ
以下その1からの続き
ローカル変数とグローバル変数
ローカル変数
- 関数の中で使用される変数(組み込み変数を除く)は、関数の呼出しごとに作成され、関数から戻る際に破棄される
- そのため、関数外からは参照できないローカル変数となる
- その結果、ローカル変数はグローバル変数と同じ名前を持ったとしても、両方とも別の内容を持つ
- 分離された関数は、同じ変数名を(安全に)使用できる
- staticでないすべてのローカル変数は、関数が戻るときに自動的に解放される(空になる)
- 関数内で作成された変数は、デフォルトではローカルだが、次の例外がある
- 以下のスクリプトは期待通りの動作をしない
ex: xの値を設定する関数を作りたい x := 0 SetX(10) MsgBox,%x% SetX(val){ x := val }
グローバル変数
- 関数外の既存のグローバル変数を参照する(または新しい関数を作成する)には、その変数を使用する前にその変数をグローバルとして宣言する必要がある
- 変数名の前に「global」と書くとその変数はグローバル変数として扱われる
- 複数の変数名を「,」で区切ってまとめて書くことができる(例はその他参照)
- 関数内で「Array%i%」のような動的変数を使用した場合、ローカル変数として扱われる
- StringSplitコマンドなどで配列を作成する場合、通常はローカル変数として作成される
- ただし、配列の最初の要素がglobal宣言されている場合は、全ての要素がグローバル変数として作成される
ex: LogToFile(TextToLog) { global LogFileName ;この関数はすでにほかの部分で値がセットされている FileAppend, %TextToLog%`n, %LogFileName% }
仮定(前提)グローバルモード
関数が大量のグローバル変数にアクセスしたり作成したりする必要がある場合、関数の最初の行に"global"という単語をつけて、すべての変数がグローバル変数になるように定義することができる
ex: SetDefaults()
{
global
MyGlobal := 33 ;グローバル変数を作成、必要に応じて変数に初期値を代入する
Global1 ;これもグローバル変数になる
local x, y:=0, z ;ローカル変数にしたい場合はこのように宣言しなければならない
}
この仮定グローバルモードは、関数がArray%A_Index%に値を代入するループなどのグローバル擬似配列を作成するために関数で使用することもできる
スーパーグローバル変数
- グローバル宣言が関数の外に出現する場合は、デフォルトですべての関数に(グローバルが)適用される
- これにより、各関数の変数を再宣言する必要がなくなる
- ただし、同じ名前のパラメータまたはローカル変数が宣言されている場合は、グローバル変数よりもそちいが優先される
- また、classキーワードによって作成された変数もスーパーグローバルになる
static変数
- static変数は常に暗黙的にローカルだが、ローカル変数との違いは呼び出し間でその値が記憶されるという点
- 宣言方法はグローバル変数と同様でいい
- 複数の変数名を「,」で区切ってまとめて書くことができる(例はその他参照)
ex: LogToFile(TextToLog) { static LoggedLines := 0 LoggedLines += 1 ; Maintain a tally locally (its value is remembered between calls). global LogFileName FileAppend, %LoggedLines%: %TextToLog%`n, %LogFileName% }
- static変数は、宣言と同じ行で := の後に任意の式をつけて初期化することができる
- たとえば、static X:=0, Y:="fox"など
- 各静的変数は1回だけ初期化される
- static変数は、スクリプトファイルに表示される順序で、スクリプトの自動実行セクションの実行前に初期化される
仮定(前提)staticモード
関数の最初の行に"static"という単語を付けることによって、すべての変数がstatic(そのパラメータを除く)であると仮定するように定義できる
ex: GetFromStaticArray(WhichItemNumber)
{
static
static FirstCallToUs := true ;static宣言のイニシャライザは、起動時に1回だけ実行される
if FirstCallToUs ;最初の呼び出しではstatic配列を作成しるが、それ以降の呼び出しでは作成されない
{
FirstCallToUs := false
Loop 10
StaticArray%A_Index% := "Value #" . A_Index
}
return StaticArray%WhichItemNumber%
}
staticモードでは、staticであってはならない変数は、ローカルまたはグローバルとして宣言する必要がある
ローカルとグローバルについてその他
- 以下の例のように、複数の変数をカンマで区切って同じ行に宣言することができる
ex: global LogFileName, MaxRetries := 5 static TotalAttempts := 0, PrevResult
- ローカル変数またはグローバル変数は、宣言と同じ行で:=の後に任意の式を付けて初期化することができる
- static初期化子とは異なり、ローカルとグローバルのイニシャライザは、関数が呼び出されるたびに実行されるが、コントロールのフローが実際にそれらに到達する場合にのみ実行される
- つまり、「local x := 0」のような行は、「local x」の宣言とそれに続く「x := 0」という2つの別々の行を書くのと同じ効果を持つということ
- ローカル、グローバル、およびstaticという単語は、スクリプトの起動時にすぐに処理されるため、変数はif文によって条件付きで宣言することはできない
- つまり、ifまたはelseのブロック内の宣言は、宣言と関数の閉じ括弧の間のすべての行に対して無条件に有効になる
- また現在、グローバル配列%i%などの動的変数を宣言することはできない
関数の動的呼び出し
- 関数(組み込み関数でさえ)は、パーセント記号で動的に呼び出すことができる
- たとえば、%Var%(x, "fox")は名前がVarに含まれる関数を呼び出す
- 同様に、 Func%A_Index%()は、A_Indexの現在の値に応じて、Func1()またはFunc2()などを呼び出す
- Var in %Var%()には、関数名または関数オブジェクトを含めることができる
- 関数が存在しない場合は、デフォルトの基本オブジェクトのCallメソッドが代わりに呼び出される(通常、このメソッドが定義されていない場合は、__Callメタ関数が呼び出される)
- 以下のいずれかの理由により関数を呼び出せない場合は、例外がスローされる
- 存在しない関数を呼び出すことはIf IsFunc(VarContainingFuncName)を使用して回避することができる
- 組み込み関数を除いて、呼び出される関数の定義は、#Includeやライブラリ関数への非動的呼び出しなどの手段によってスクリプトに明示的に存在しなければならない
- IsFunc()の戻り値(必須パラメータの数に1を足した値)をチェックすることによって避けることができるパラメータは少ない
- 注:あまりにも多くのパラメータを渡すことは許容されるが、余分なパラメータは完全に評価され(関数への呼び出しを含む)、破棄される
- 関数への動的呼び出しは、スクリプトの実行が開始される前に通常の呼び出しが参照されるため、通常の呼び出しよりもわずかに遅くなる
再帰呼び出し
- 関数の中からその関数自身を呼び出すテクニックを再帰呼び出しという
- 下記の例は、パラメータで与えられたnの階乗を求める関数である
ex: Factorial(n){ If n := 1 Return 1 Else Return n * Factorial(n * 1) }
ex: Factorial(ByRef n){ If n := 1 ;(2)ここのnでは呼び出し元のxではなく、呼び出された側のxが参照されてしまう Return x := n * 1 Factorial(x) ;(1)ここでローカル変数xを参照渡しすると n *:= x }
boolean型の短絡評価
- 式の中でAND、OR、および3項演算子が使用されている場合、それらは短絡してパフォーマンスを向上させる(関数呼び出しが存在するかどうかにかかわらず)
- 短絡は、最終結果に影響を及ぼし得ない部分の評価を省略すること
- 概念の説明
ex: if (ColorName <> "" AND not FindColor(ColorName)) MsgBox %ColorName% could not be found.
- 上記の例では、ColorName変数が空の場合、FindColor()関数は呼び出されない
- これは、ANDの左側が偽であるため、右側が最終結果を真にすることができないから
- この振る舞いにより、関数がグローバル変数の内容を変更するなどの副作用が起こる
- また、副作用はその関数がANDまたはORの右側で呼び出されると、決して発生しないことを認識することが重要
- また、短絡評価がネストされたANDおよびORにカスケードすることにも注意する必要がある
- たとえば、次の式では、ColorNameが空白のときは常に一番左の比較が行われるこれは、左側が確実に最終的な答えを決定するのに十分であるから
ex: if (ColorName = "" OR FindColor(ColorName, Region1) OR FindColor(ColorName, Region2)) break ; Nothing to search for, or a match was found.
- 上記の例で示されているように、パフォーマンスの向上のために、ANDまたはORの右側では、関数が呼び出されないことがある
- この技法は、パラメータの1つが空の文字列など、不適切とみなされる値を渡されるときに関数が呼び出されないようにするためにも使用できる
- 三項条件演算子(?:)も、不必要な分岐を評価しないことによって短絡する
関数内でのサブルーチンの使用
- 関数は他の関数の定義を含むことはできないが、サブルーチンを含めることができる
- 他のサブルーチンと同様に、Gosubを使用してそれらを起動し、Returnで戻る(この場合、ReturnはGosubに属し、関数ではない)
- 関数がGosubを使用して関数外にあるサブルーチンにジャンプする場合、外部の変数はすべてグローバルであり、サブルーチンが戻るまで関数のローカル変数にはアクセスできまない
- ただし、A_ThisFuncには引き続き関数の名前が含まれる
- Gotoは関数の内部から外部にジャンプすることはできないが、Gosubに外部/パブリックサブルーチンを関数として渡し、そこからGotoを実行することは可能
- Gotoの使用は一般的には推奨されないが、関数内で同じ関数内の別の位置(ラベル)にジャンプすることがでる
- これにより、多くのリターンポイントを持つ複雑な関数を簡単にすることができる
- また、関数外のラベルにGotoでジャンプしようとした場合、その行は無視される
- これらの関数のすべては、返される前にクリーンアップを行う必要がある
- 関数には、タイマー、GUI g-label、メニュー項目などの外部呼び出しサブルーチンが含まれている場合がある
- これは#Includeで使用し、別のファイルにカプセル化するために一般的に行われ、スクリプトの自動実行セクションとの干渉を防げる
- ただし、次の制限が適用される
- このようなサブルーチンでは、関数が正常に呼び出される場合は、ローカル変数ではなくstatic変数のみを使用する必要がある
- これは、関数呼び出しスレッドを中断するサブルーチンスレッド(またはその逆)が中断されたスレッドによって見られるローカル変数の値を変更できるため
- さらに、関数が呼び出し元に戻ると、そのローカル変数はすべて空白になってメモリが解放される
- このようなサブルーチンでは、GUI制御変数としてグローバル変数(static変数ではない)のみを使用する必要がある
- 関数がサブルーチンスレッドによって入力されると、そのスレッドによって作成された動的変数への参照はすべて(配列を作成するコマンドを含む)グローバルとして扱われる
- 同様に、ローカルラベルは動的に参照することはできない
Return、Exit、および一般的な備考
- 関数内の実行フローが関数の閉じ括弧(関数の終了地点)に到達する前にReturnに到達すると、関数は終了し、空の値(空文字列)を呼び出し元に返す
- 関数が明示的にReturnのパラメータを省略するときも、空の値が返される
- 関数がExitコマンドを使用して現在のスレッドを終了すると、その呼び出し元は戻り値をまったく受け取らない
- たとえば、Var= Add(2,3)ステートメントは、Add()が終了するとVarを変更しない
- 存在しないファイルを実行するなどのランタイムエラーが発生した場合(UseErrorLevelが有効でない場合)も同じことが起こる
- 関数は、覚えやすい余分な値を返す目的で、ErrorLevelの値を変更することがある
- 空白の値(空文字列)が1つ以上ある関数を呼び出すには、以下の例のように空のクォートペアを使用する
ex: FindColor(ColorName、 "")
- 関数を呼び出すことは新しいスレッドを開始しないので、SendModeやSetTitleMatchModeなどの設定に対して関数が行った変更は、呼び出し元に対しても有効である
- 関数の呼び出し元は、存在しない変数または配列要素を渡すことがありるこれは、関数が対応するパラメータがByRefであると期待している場合に便利
- たとえば、GetNextLine(BlankArray%i%)を呼び出すと、呼び出し元が関数内にあるかどうか、および仮定グローバルモードが有効かどうかによって、変数BlankArray%i%がローカルまたはグローバルとして自動的に作成される
- 関数内で使用すると、ListVarsは関数のローカル変数とその内容を表示するこれは、スクリプトのデバッグに役立つ
スタイルと命名規則
- 特別な変数に別個の接頭辞が与えられていると、複雑な関数が読みやすく保守的になることがある
- たとえば、関数のパラメータリストの先頭に "p"または "p_"を付けて名前を付けると、特別な性質が一目でわかりやすくなる
- 同様に、接頭辞「r」または「r」はByRefパラメータに使用でき、「s」または「s」はstatic変数に使用できる
- One True Brace(OTB)スタイルは、オプションで関数を定義するために使用できる
ex: Add(x,y){ Return x + y }
#Includeを使用して複数のスクリプト間で関数を共有する
- #Includeディレクティブは、外部ファイルから関数をロードするために(スクリプトの先頭でも)使用できる
関数のライブラリ:標準ライブラリとユーザライブラリ
ex: %A_ScriptDir%\Lib\ ; ローカルライブラリ %A_MyDocuments%\AutoHotkey\Lib\ ; ユーザーライブラリ path-to-the-currently-running-AutoHotkey.exe\Lib\ ; 標準ライブラリ
- たとえば、存在しない関数MyFunc()を呼び出すと、プログラムはユーザーライブラリ内の"MyFunc.ahk"という名前のファイルを検索する
- そこで見つからなければ、標準ライブラリで検索される
- まだ見つからず、関数の名前にアンダースコア(たとえばMyPrefix_MyFunc)が含まれている場合、プログラムは両方のライブラリでMyPrefix.ahkという名前のファイルを検索し、存在する場合は読み込まれる
これにより、MyPrefix.ahkに、関数MyPrefix_MyFuncと名前がMyPrefix_で始まる他の関連関数の両方が含まれるようになる
ローカルライブラリは、ユーザライブラリと標準ライブラリの前に検索される
MyFunc()のような直接的な関数呼び出しだけでは、ライブラリを自動インクルードすることができる
タイマーやGUIイベントなど、関数が動的にまたは間接的にしか呼び出されない場合は、ライブラリーをスクリプトに明示的に組み込む必要がある
ライブラリファイルには通常、ファイル名と同じ名前の関数が1つしか含まれていませんが、プライベート関数とそれによってのみ呼び出されるサブルーチンも含まれている
ライブラリファイルが#Includeを使用する場合、#Includeの作業ディレクトリはライブラリファイル自身のディレクトリになる
これは、その関数とそれに関連する他の関数を含む大きなライブラリファイルへのリダイレクトを作成するために使用できる
- ただし、AutoHotkey.exeのコピーは、コンパイラディレクトリの上のディレクトリに存在する必要がある(通常の状態)
AutoHotkey.exeが存在しない場合、コンパイラは引き続き動作しるが、ライブラリ関数は自動的には使用できない
ライブラリから取り込まれた関数は、スクリプトの実行が開始される前にプリロードされているため、他の関数と同様に機能する
組み込み関数
- 組み込み関数のパラメータリストの末尾にある任意のオプションパラメータは、完全に省略することができる
- たとえば、WinExist("Untitled - 注意pad")は、他の3つのパラメータが空白とみなされるため有効
- スクリプトが同じ名前の独自の関数を定義している場合、組み込み関数はオーバーライドされる
- たとえば、標準のスクリプトの代わりに呼び出される独自のカスタムWinExist()関数をスクリプトに含めることができる
- ただし、スクリプトは元の関数を呼び出す方法はない
- DLLファイルに存在する外部関数は、DllCall()で呼び出すことができる
- 下の特定の組み込み関数の詳細については、各リンク先を参照(後日)
組み込み関数一覧
よく使うもの
使い始めでもよく使ってるから実際結構使うんじゃない
関数名 | 関数の説明 |
---|---|
FileExist | ファイルまたはフォルダが存在するかを判定してその属性を返す |
GetKeyState | 指定したキーが下げられている場合はtrue(1)を下げられてない場合はfalse(2)を返す |
InStr | 指定した文字列が存在するかを右または左から検索 |
RegExMatch | 文字列にパターン(正規表現での指定)が含まれているか判定 |
RegExReplace | 文字列内のパターン(正規表現での指定)を置換する |
StrLen | 文字列の文字数の取得 |
StrReplace | 指定した文字列を置換する(正規表現でない) |
StrSplit | 指定した区切り文字を使用して、文字列を分割して、配列にする(配列が返る) |
SubStr | 文字列の指定した位置の文字を取得 |
WinActive | 指定したウィンドウが存在していて、アクティブなのかを判定し、ID(HWND)を返す |
WinExist | 指定したウィンドウが存在するかを判定し、ID(HWND)を返す |
その他の関数
Dllcallとかは使いそうだけどまだ使ったことないのばかり
関数名 | 関数の説明 |
---|---|
Chr | 指定した番号の文字コードに対応する文字列を返す |
DllCall | 標準のWindows API関数など、DLL内の関数を呼び出す |
FileOpen | オブジェクト指向のファイルのI/Oが可能 |
Func | 指定された関数への参照を取得 |
GetKeyName/VK/SC | キー名/仮想キーコード/スキャンコードを取得 |
IsByRef | 指定した関数内の変数がByRefかどうかを判定し、そうであれば0以外を返す |
IsFunc | 指定した関数がスクリプト内に存在するか判定し、そうであれば0以外を返す |
IsLabel | 指定されたラベルがスクリプト内に存在するか判定し、そうであれば0以外を返す |
IsObject | 指定された値がオブジェクトかどうか判定し、そうであれば0以外を返す |
ListView | ListViewの行/列を追加/挿入/変更/削除を行う、またはデータの取得ができる |
NumGet | 指定されたアドレス+オフセットに格納されているバイナリ値を返す |
NumPut | 指定されたアドレス+オフセットにバイナリ形式の数値を格納する |
OnMessage | メッセージ/イベントを監視する |
Ord | 指定された文字列の最初の文字の文字コード(数値)を返す |
StrGet | メモリアドレスから文字列をコピーする、必要に応じてコードページ間で変換する |
StrPut | 文字列をメモリアドレスから文字列にコピーする、必要に応じてコードページ間で変換する |
RegisterCallBack | 呼び出し時にスクリプト内の関数に呼び出しをリダイレクトとするマシンコードアドレスを作成する |
TreeView | TreeViewアイテムの追加/挿入/変更/削除を行う、またはデータの取得ができる |
Trim | 文字列の先頭及び/または末尾の文字を切り除く |
VarSetCapacity | 変数のメモリの保持容量を拡大する、またはメモリの開放ができる |
数学関連
パラメータは基本的に数をとる
関数名 | 関数の説明 |
---|---|
Abs | パラメータ(数)の絶対値を返す |
Ceil | パラメータ(数)を最も近い整数(.00のように小数点以下はつかない)になるように切り上げた数値を返す |
Exp | e(ネイピア数)を返す |
Floor | パラメータ(数)を最も近い整数(.00のように小数点以下はつかない)になるように切り捨てた数値を返す |
Log | パラメータ(数)に対する常用対数(底10)を返す |
Ln | パラメータ(数)に対する自然対数(底e)を返す(eはネイピア数) |
Mod | パラメータ(数)を指定した数値での剰余を返す |
Round | 小数点以下を第四位までになるように丸める |
Squrt | パラメータ(数)の平方根を返す |
Sin/Cos/Tan | パラメータ(数)の三角関数のサイン(正弦)/コサイン(余弦)/タンジェント(正接)を返す |
ASin/ACos/ATan | パラメータ(数)の逆三角関数のアークサイン/アークコサイン/アークタンジェントをラジアンで返す |