関数

関数とは、引数として値を受け取り、何らかの処理を行い、結果を返すサブルーチンである。
通常のコマンドと違い、の中に組み込んで使うことが出来る。

関数の定義方法

関数の定義は、以下のようにする。
関数の内容が1行だけでも、「{」「}」は省略できない。

関数名(引数リスト){
関数本体
}

関数名には、半角英数字と「_」(アンダーバー)などが使用できる。
引数リストには、引数の名前やタイプ、デフォルト値を「,」で区切って列挙する。
関数が引数を必要としない場合、引数リストは空にする。
関数名と「(」の間には、スペースなどを入れてはいけない。
関数内では、引数として与えられた値を変数のように参照することができる。
「return」に続いて記述したが返り値として呼び出し元に返される。
returnがない場合、関数の最後の「}」まで実行された時点で、空の文字列が返される。

下記の例では、Addという関数名の関数を定義している。
Add関数はxとyの2つの引数を受け取り、xとyを足した値を返り値として返す。

Add(x, y){
	return x + y
}

関数の定義は、関数定義の内部以外ならどこに書いてもかまわない。
スクリプト実行中に、関数定義の行に実行が移った場合、関数定義の終わりまでがスキップされ、実行はされない。

関数の呼び出し

関数名に続いて「()」の中に引数を「,」区切りで列挙したものが関数呼び出しである。
関数呼び出しは、式の中に組み込んで使用できる。

Sum:=Add(2,3)

上記の例では、Add関数の引数xとして「2」、引数yとして「3」を与えて呼び出し、その結果を変数「Sum」に代入している。

Add(Add(1,2),3+4)」のように、関数呼び出しの引数に式や関数呼び出しを記述することも可能。
その場合、まず内側の式が左の引数から順に計算され、次にそれらを引数として外側の関数が実行される。

変数に代入せず、関数の呼び出しだけを行うことも可能である。
下記の例では、2つの引数の和をダイアログ表示する関数を定義し、引数に2と3を与えて呼び出している。

ShowAdd(x,y){
	MsgBox,% x+y
}
ShowAdd(2,3)

関数の中からほかの関数を呼び出すことも可能である。
呼び出しの深度の上限は159回で、160回目の呼び出しをしようとするとAutoHotkeyのプログラムが不正終了する。

動的関数名での呼び出し

関数呼び出しの関数名部分には、「%FuncName%()」や「Func%Index%()」のように変数参照を組み込むことが出来る。
この場合、実行時にその変数を展開した名前の関数が呼び出される。
例えば、「Index」が「1」の時に「Func%Index%()」が実行されると、「Func1()」という関数が呼び出される
もしその関数が存在しなかったり、引数の数が合わなかった場合、関数の結果は長さゼロの文字列になる。

引数のデフォルト値

Add(p1,p2,p3=0,p4=0,p5=0){
	return p1+p2+p3+p4+p5
}

上記のように、引数リストの引数名の後に「=」に続いてデフォルト値を記述することで、引数を省略した場合にはその値が使われるようにすることが可能である。
上記の例では、「Add(1,2)」とした場合は3が返り、「Add(1,2,3)」とすれば6が返る。
デフォルト値として設定できるのは、「10」や「1.001」、「1.11e+10」、「""」、「"this is default value"」のような定数、true、falseのみである。
複数の引数を省略することは可能だが、引数の途中だけを省略することはできない。上記の例では、「Add(1,2,,3)」とすることはできない。
また、これに伴い、「x, y=0, z」のようにデフォルト値を持つ引数の後に、デフォルト値を持たない引数を設定することはできない。

引数の参照渡し

関数に値ではなく変数の参照を渡すことを参照渡しと言う。
参照渡しを利用すると、関数から呼び出し元の変数を変更することができる。
これを利用すれば、2つ以上の情報を呼び出し元に知らせることが可能になる。
引数を参照渡しにするには、引数リストで引数名の前に「ByRef 」をつける。
参照渡しになっている引数に変数以外の式を指定すると実行時エラーになる。未定義の変数を指定することは可能。

下記の例では、2つの変数の内容を入れ替える関数「Swap」を定義している。

Swap(ByRef Left, ByRef Right){
	temp := Left
	Left := Right
	Right := temp
}

参照渡しでは、関数呼び出し時に変数の内容のコピーが行われないため、大きな文字列を引数に渡すときの効率がよくなる。

ByRef引数にもデフォルト値を指定できる。
引数が省略された場合は、デフォルト値を格納したローカル変数が作成される。

変数の有効範囲

通常の関数では、関数の中で使用される関数の中で使用される変数(組み込み変数を除く)は、関数内でのみ有効なローカル変数となる。 ローカル変数は、関数の呼出しごとに作成され、関数から戻る際に破棄される。
よって、以下のスクリプトは期待通りの動作をしない。

;xの値を設定する関数を作りたい
SetX(val){
	x:=val
}
x:=0
SetX(10)
MsgBox,%x%

グローバル変数の利用

関数内から通常の変数(グローバル変数)にアクセスするには、「global」に続いてアクセスしたい変数名を記述する。複数の変数名を「,」で区切ってまとめて書くことができる。
以下のスクリプトは期待通りの動作になる。

;xの値を設定する関数を作りたい
SetX(val){
	global x
	x:=val
}
x:=0
SetX(10)
MsgBox,%x%

また、変数の使用を宣言すると同時に、変数に値を代入することも出来る。
この時、「=」演算子は、比較演算子ではなく「:=」演算子とみなされる。

SetX(val){
	global x=val
}

条件分岐を使用しても、条件によって変数をグローバルにするかローカルにするかを切り替えることは出来ない。

デフォルトの有効範囲をグローバルにする

関数の最初に「global」とだけ書いた行があると、関数内に記述された全ての変数がグローバル変数となる。
このとき、「local」に続いて変数名を書いた行があると、その変数だけローカル変数となる。
グローバル変数の使用宣言と同様、複数の変数名を「,」で区切ってまとめて書いたり、宣言と同時に代入することも可能。

SetX(val){
	global
	x=val
}

関数の最初に「global」が無くても、「local」の変数宣言があれば、それ以外の変数はグローバル変数として扱われる。

関数内で「Array%i%」のような動的変数を使用した場合、ローカル変数として扱われる。
ただし、その名前のローカル変数が存在せず、グローバル変数なら存在する場合、そのグローバル変数が使われる。
ローカルにもグローバルにも存在せず、変数を新たに作成しなければならない場合、デフォルトの有効範囲で作成される。

StringSplitコマンドなどで配列を作成する場合、通常はローカル変数として作成される。
ただし、配列の最初の要素がglobal宣言されている場合は、全ての要素がグローバル変数として作成される。

スタティック変数

同じ関数の中で共有される変数を作成するには「static」に続いて変数名を書く。複数の変数名を「,」で区切ってまとめて書くことができる。
スタティック変数は、関数の中でしか参照できないが、同じ関数の間でひとつの変数が共有される。

AAA(){
	static CalledTimes:=0
	CalledTimes++
	MsgBox,%CalledTimes%回目
}

スタティック変数の初期化では、数値や文字列の定数を代入することのみ可能。
スタティック変数は、スクリプトの起動時に一度だけ初期化される。

再帰呼び出し

関数の中からその関数自身を呼び出すテクニックを再帰呼び出しという。
下記の例は、引数で与えられたnの階乗を求める関数である。

Factorial(n){
	If n=1
		return 1
	else
		return n*Factorial(n-1)
}

なお、再帰呼び出しを行う関数で、「ByRef」による参照渡しの引数にローカル変数を与えると、呼び出し先の当該変数が参照されるようになってしまう。
よって、以下のスクリプトは正しく動作しない。

Factorial(ByRef n){
	If n=1			;(2)ここのnでは呼び出し元のxではなく、呼び出された側のxが参照されてしまう
		return
	x:=n-1
	Factorial(x)	;(1)ここでローカル変数xを参照渡しすると
	n*=x
}

関数内からのGosub/Goto/Exit

関数内からは、関数の内外のサブルーチンを「Gosub」で呼び出すことができる。
関数外のサブルーチンを呼び出した場合、呼び出されたサブルーチンからは関数内のローカル変数は呼び出せない。一方、関数に「global」が宣言されていなくても、全てのグローバル変数を利用できる。

関数内からは、関数内のラベルにのみ「Goto」でジャンプできる。
関数外のラベルにGotoでジャンプしようとした場合、その行は無視される。

関数内で実行中のスレッドを終了する「Exit」コマンドを実行すると、関数の呼び出し元に戻ることなくその時点でスレッドが終了する。

関数ライブラリスクリプト

ライブラリスクリプトは「%A_MyDocuments%\AutoHotkey\Lib\」か、AutoHotkey.exeのあるフォルダ内の「Lib」フォルダに格納する。
スクリプトファイル名の内、拡張子を除いた名前部分がライブラリ名となる。
ライブラリスクリプトには、ライブラリ名と同じ名前の関数や、ライブラリ名の後に「_」に続いて任意の文字列の付いた名前の関数(例:「MyLib_FuncA()」)を記述する。
それ以外の関数やラベルも定義でき、読み込み元スクリプトから呼び出せるが、名前が重複しないように注意する必要がある。

スクリプト読み込み時、スクリプト中で定義されていない関数の(非動的関数名での)呼び出しが記述されていると、その関数名に「.ahk」を付けたファイル名のスクリプトがライブラリフォルダから検索され、もし存在すれば追加で読み込まれる。
該当するファイルが存在せず、関数名に「_」が含まれる場合、最後の「_」の前までをファイル名としたファイルが検索される。
これらのファイルを読み込んでも、該当する関数が定義されていない場合は、スクリプト読み込みエラーになる。

読み込まれたスクリプトは、元のスクリプトの末尾で#Includeされたのと同じ状態になる。
ライブラリスクリプト内で、関数の外にサブルーチンラベルなどを記述すると、意図されていない状況で実行されてしまう場合がある。
ライブラリスクリプトの先頭にreturnを記述しておけば防止できる。

ライブラリスクリプト内でも、他のライブラリスクリプトの関数を呼び出し可能である。

ライブラリスクリプト内に#Includeを記述した場合、パスの基準フォルダはそのライブラリスクリプトがあるフォルダになる。

ahk2exe.exeでコンパイルする場合、使用されているライブラリスクリプトも読み込んでコンパイルされる。 この時、ahk2exe.exeは、AutoHotkey.exeがあるフォルダ内の何らかのフォルダ(通常は「Compiler」)に置かれている必要がある。

関数によるスクリプトのモジュール化

関数の定義だけを書いたスクリプトを「#Include」指令で読み込んで使うようにすることで、スクリプトの保守性を向上させられる。
他の人が作ったスクリプトを自分のスクリプトに組み込みたい場合などに、変数名の重複を気にしなくてすむので便利である。

組み込み関数

AutoHotkeyには、あらかじめ定義された組み込み関数が用意されている。
これらは、普通の関数と同じように使用できる。
定義されている組み込み関数については、コマンドリファレンスを参照。

組み込み関数は、同じ名前の関数を定義することで上書きできる。