のら(NORA)のブログ

主にソフトについて備忘録で(手抜き)書いていますねー。困った人の役とかに立てばいいかなー。とかそういう程度の奴でーす。むぎゅぎゅ~。更新停滞中ですね~。

autohotkey2.0勉強中(翻訳)その9~オブジェクト編その1~

最終更新日:2017/12/23

目次

はじめに

その1のコピペ

翻訳とかあんま得意じゃないんだけど
間違いあったらコメくれれば訂正します。というか自分も間違えて覚えてしまうから(´・ω・`)

一応各説明ごとに例は必ず一つ入れている
手抜きだからかなり見にくいかも
定期的に修正入ると思う

参考:ahkwiki日本版様及び公式ヘルプ

オブジェクト

オブジェクトの基本

AutoHotkeyのオブジェクトは、以下の3つの基本機能を提供する抽象データ型だってさ

  • 値の取得
  • 値の設定
  • メソッド(つまり、対象オブジェクトで何かを行う関数)を呼び出す

オブジェクト参照は、特定のオブジェクトに対するポインタまたは「ハンドル」
文字列や数値と同様に、オブジェクト参照は変数に格納され、関数に渡されるか、関数から返され、オブジェクトに格納される
x := yのようにある変数から別の変数への参照をコピーした後、両方の変数が同じオブジェクトを参照することになる
参照がコピーされるだけなので、別の物が作成できるわけではないので注意
IsObjectを使用して、値がオブジェクトであるかどうかを判断できる

ex:  result := IsObject(式)

オブジェクトの種類

  • Object - スクリプト可能な連想配列
  • File - ファイル入出力用のインターフェース
  • Function Object - Func、BoundFunc、またはユーザ定義
  • ComObject - IDispatchインターフェイス(COMまたは "Automation"オブジェクト)をラップする

基本的な使い方

配列

配列その者についての説明がないのね、まあ図がないとわかりにくいから割愛で

  • 配列の作成方法

ex:

Array := [Item1, Item2, ..., ItemN]
Array := Array(Item1, Item2, ..., ItemN)  
  • 配列内の値の取り出し
ex: Value := Array[Index]
  • 配列に値を代入(格納)する
ex: Array[Index] := Value
  • 特定のインデックスに一つまたは複数の値の代入(格納)する
ex: Array.InsertAt(Index, Value, Value2, ...)
  • 一つまたは複数の要素を追加する
ex: Array.Push(Value, Value2, ...)
  • 一つまたは複数の要素を削除する
ex: RemovedValue := Array.RemoveAt(Index: 
  • 最後の要素を削除する
ex: RemovedValue := Array.Pop()

配列が空でない場合、MinIndex/MaxIndex/Length は、それぞれ配列内で現在使用されている最初(最小)の要素番号/最後(最大)の要素番号/要素数 を返す
最初の要素番号はほぼ常に1なので、MaxIndexは通常要素の数を返す
ただし、整数キーがない場合、MaxIndexは空の文字列を返し、Lengthは0を返す
配列の内容をループするには、インデックスまたはForループを使用しる

ex:

array := ["one", "two", "three"]  ;配列の宣言  

方法その1  
Loop % array.Length()  ;1から配列の最後まで繰り返す  
MsgBox % array[A_Index]

方法その2  
For index, value in array  ;配列の内容の出力  
MsgBox % "Item " index " is '" value "'"

連想配列

  • 連想配列は、ユニークなキーの集合と値の集合を含むオブジェクト
  • 各キーは1つの値に関連付けられている
  • キーには文字列、整数、オブジェクトを指定できる

  • 連想配列の作成方法

    ex:

    Array := {KeyA: ValueA, KeyB: ValueB, ..., KeyZ: ValueZ} ;方法その1 Array := Object("KeyA", ValueA, "KeyB", ValueB, ..., "KeyZ", ValueZ) ;方法その2

    • {key:value}を使用すると、一単語のみで構成されるキーでは引用符を省略できる
    • どんな式もキーとして使用できるが、変数をキーとして使用するには、カッコで囲む必要がある

      ex:{(KeyVar):Value}と{GetKey():Value}

  • 要素を取得する

    ex: Value := Array[Key]

  • 要素を割り当てる

    ex: Array[Key] := Value

  • 要素を削除する

    ex: RemovedValue := Array.Delete(Key)

  • 要素を列挙する

    ex:
    array := {ten: 10, twenty: 20, thirty: 30} For key, value in array MsgBox %key% = %value%

その他
  • 連想配列はまばらに取り込まれる可能性がある - わけわかんない
  • つまり、{1: "a",1000: "b"}には1000ではなく、2つのキーと値のペアのみが含まれる

オブジェクト

Object.LiteralKeyを使用して、プロパティ、配列要素またはメソッドにアクセスできる
LiteralKeyは識別子または整数、Objectは任意の式
識別子は、英数字、アンダースコア、非ASCII文字が使えて、引用符で囲まれていない文字列で記述する
ドットの後にスペースがあってはならない

ex: 下記はそれぞれどちらも同じ扱いになる

match.Pos  match["Pos"]  
arr.1  arr[1]
  • プロパティ(値)の取得

    ex: Value := Object.Property

  • プロパティ(値)の設定

    ex: Object.Property := Value

  • メソッドを呼び出す

    ex: ReturnValue := Object.Method(Parameters)

  • 計算されたメソッド名を持つメソッドを呼び出す

    ex: ReturnValue := Object[MethodName](Parameters)

  • COMオブジェクトとユーザー定義オブジェクトの一部のプロパティの設定

    ex:
    Value := Object.Property[Parameters] Object.Property[Parameters] := Value

既知の制限
  • 現在、x.y[z]() はサポートされていなくて、x["y", z]()として扱われる
  • 回避策として、(x.y)[z]()とすれば、最初にx.yが評価され、結果をメソッド呼び出しのターゲットとして使用される
  • x.y[z].Call()には(x.y[z]).Call()と同じ評価が行われるため、この制限はない

オブジェクトの開放

  • スクリプトはオブジェクトを明示的に解放しない(メモリから消えないと同義と考えていいかな)
  • オブジェクトへの最後の参照が解放されると、オブジェクトは自動的に解放される
  • 変数に格納されているオブジェクトの参照は、その変数に他の値が割り当てられたときに自動的に解放される

ex:

obj := {}  ; オブジェクトの設定
obj := ""  ; 上で設定したオブジェクトの開放
  • 同様に、別のオブジェクトのフィールドに格納されている参照は、そのフィールドに他の値が割り当てられるかオブジェクトから削除されると解放される
    • これは実際にはオブジェクトである配列にも当てはまる

ex:

arr := [{}]  ; Creates an array containing an object.
arr[1] := {}  ; Creates a second object, implicitly freeing the first object.
arr.RemoveAt(1)  ; Removes and frees the second object.
  • オブジェクトが解放される前に、オブジェクトへのすべての参照が解放されなければならなく、循環参照を含むオブジェクトは自動的に解放されない
  • たとえば、x.childがyを参照し、y.parentがxを参照する場合、親オブジェクトにまだ子への参照が含まれているため、xとyを消去するだけでは十分ではなく、この状況を解決するには、循環参照を削除すればよい

ex:

x := {}, y := {}             ; 二つのオブジェクトの作成
x.child := y, y.parent := x  ; 循環参照の作成

y.parent := ""               ; 循環参照がある場合先に開放しないといけない
x := "", y := ""             ; 一つ↑の文がないと開放されない
  • より高度な使い方と詳細については、参照カウントを参照(後ほど)

備考

構文

すべてのタイプのオブジェクトは、配列構文(角括弧)とオブジェクト構文(ドット)の両方をサポートする
さらに、オブジェクト参照自体を式で使用することもできる

  • オブジェクト参照が、= ==!= <>のいずれかを使用して他の値と比較される場合、両方の値が同じオブジェクトへの参照である場合にのみ、それらは等しいとみなされる
  • if obj、 !obj、 obj ? x : y.のように、booleanが必要な場合、オブジェクトは常に真と見なされる
  • オブジェクトのアドレスは、「&」演算子(アドレス参照演算子)を使用して取得できる
  • これは、オブジェクトの作成時点から最後の参照が解放される瞬間まで、オブジェクトを一意に識別することができる
  • オブジェクトが想定されていない構文でオブジェクトが使用される場合、そのオブジェクトは空の文字列として扱われる
  • オブジェクトの中身が変更されてしまうのでこのような使い方はしないこと

ex:

「MsgBox %object%」は空のメッセージを出力し、「object + 1」は空の文字列を生成されることになる
  • オブジェクトメソッドの呼び出しの直後に代入演算子がある場合、それはパラメータでプロパティを設定するのと同ことになる
  • x.y += 1、--arr[1]などの複合割り当てがサポートされている

ex: 以下は同等

obj.item(x) := y
obj.item[x] := y
  • プロパティを取得または設定するときは、パラメータを省略できる →ex1
  • メソッド名も完全に省略することもできる →ex2
  • スクリプトは、プロパティおよびメタ関数のパラメーターのデフォルト値を定義することで省略可能
  • スクリプトは、_Callメタ関数の最初のパラメータのデフォルト値を定義することによって省略可能
  • COMオブジェクトを呼び出すときにプロパティまたはメソッド名が省略された場合、その「既定のメンバ」が呼び出される

ex:

1. x[,2].
2. x[](a).
キー

[]、{}または新しい演算子で作成されたオブジェクトでは、文字列、整数、およびオブジェクトをキーとして使用できるが、次のような注意点がある
キーの値は保持されるが、その値の型の識別は保持されない
つまり、数値が同じであれば(数値文字列の書式も含めて)、整数は文字列として格納することも、その逆も可能になるということ

具体的には
* 整数キーは、可能な場合はネイティブ符号付き整数型を使用して格納される * -2147483648より小さいか、または2147483647より大きい整数は、AutoHotkey 32ビットに文字列として格納されるが、AutoHotkey 64ビットでは整数として格納される(対照的に、64ビット整数はどちらのバージョンでも値として格納できる) * 文字列キーを(符号付き整数型の)整数に変換し、データを失うことなく文字列に戻すことができる場合は、整数として格納される * 言い換えれば、「x[16]」と「x["16"]」は同じだが、「x["0016"]」や「x["0x10"]」とは異なる * しかし、ロード時に数値リテラルは純粋な数値に変換されるため、x [0x10]はx ["16"]と等価 * 浮動小数点数はキーとしてサポートされておらず、代わりに文字列に変換される * 一貫性と明確さのために、スクリプト浮動小数リテラルをキーとして使用しないこと

下記のような文字列は一般的にキーとして使用すべきではない
* デフォルトでは、文字列キー "base"はオブジェクトの基本オブジェクトを取得または設定するために使用されるため、通常の割り当てで保存するためには使用できない * ただし、値がObjRawSet(Object, "base", "")またはObject.SetCapacity("base", 0)などの他の手段によって格納されている場合、キー "base"は他の文字列と同じように動作する * 「Length」などの組み込みメソッド名はキーとして使用できるが、値を格納すると、対応するメソッドが呼び出されなくなってしまう(ObjLengthなどの適切な関数への参照でない限り)

Tips(応用的な使用法)

関数の参照

変数funcに関数名が含まれている場合、関数は%func%()という式で呼び出すことができる
しかし、これは毎回関数名を解決する必要があり、関数が複数回呼び出されると非効率になってしまう
パフォーマンスを向上させるために、関数への参照を取得し、後で使用できるように格納することができる →ex1
また、次の構文を使用して参照することで呼び出すことができる →ex2
関数参照のその他のプロパティの詳細については、Funcオブジェクトを参照(後ほど)

ex:

1. MyFuncRef := Func("MyFunc")

2. RetVal := %MyFuncRef%(Params)
RetVal := MyFuncRef.Call(Params)
配列の配列(多次元配列)

AutoHotkeyは、他の配列の中に配列を透過的に格納することによって、 "多次元"配列をサポートしている
たとえば、表は行の配列として表現でき、各行はそれ自体が列の配列になる
その場合、行xの列yの内容は、以下のいずれかの方法を使用して設定できる

ex:

table[x][y] := content  ; 方法A
table[x, y] := content  ; 方法B

下記の例では、table[x]が存在しない場合、AとBは次の2つの点で異なるものになる * Aは失敗し、Bは自動的にオブジェクトを作成し、テーブル[x]に格納される * テーブルのbaseがメタ関数を定義している場合は、次のように呼び出される * それにより、Bではオブジェクトが割り当てられ、全体のカスタム動作を定義することができる

ex:

table.base.__Get(table, x)[y] := content   ; 方法A
table.base.__Set(table, x, y, content)     ; 方法B

table[a,b,c,d] := value などの多次元の割り当ては、次のように処理される * 残っているキーが1つだけの場合は、割り当てを実行して戻ることになる、層でない場合は以下の動作が繰り返される * リスト内の最初のキーをオブジェクトで検索される * オブジェクトでないものが検出された場合は、失敗する(例外をスローしる) * オブジェクトが見つからない場合は、オブジェクトを作成して保存される * サブオブジェクトを再帰的に呼び出し、残りのキーと値を渡す

また、この動作は、スクリプト作成オブジェクトにのみ適用され、COMオブジェクトやCOM配列などの特殊なタイプのオブジェクトには適用されない

関数の配列

単純に関数の参照や関数名を含むもののこと

ex:

array := [Func("FirstFunc"), Func("SecondFunc")]

; "foo"をパラメータとして両方の関数を呼び出している
Loop 2
array[A_Index].Call("foo")

; 暗黙的に配列自身をパラメータとして両方の関数を呼び出している
Loop 2
array[A_Index]()

FirstFunc(param) {
MsgBox % A_ThisFunc ": " (IsObject(param) ? "object" : param)
}
SecondFunc(param) {
MsgBox % A_ThisFunc ": " (IsObject(param) ? "object" : param)
}

カスタムオブジェクト

  • スクリプトによって作成されたオブジェクトには、あらかじめ定義された構造である必要はない
  • 代わりに、各オブジェクトは基本オブジェクト(「プロトタイプ」または「クラス」とも呼ばれる)からプロパティとメソッドを継承できる
  • プロパティとメソッドはいつでもオブジェクトに追加(またはオブジェクトから削除)することができ、これらの変更は任意のすべての派生オブジェクトに影響を与える
  • より複雑または特殊な状況では、基本オブジェクトは、メタ関数を定義することによって派生したすべてのオブジェクトの標準的な動作を上書きできる
  • 基本オブジェクトは普通のオブジェクトであり、通常は次の2つの方法のいずれかで作成される

ex:

1. class baseObject {
static foo := "bar"
}

2. baseObject := {foo: "bar"}

別のオブジェクトから派生したオブジェクトを作成するために、スクリプトは基本プロパティに割り当てることも、新しいキーワードを使うこともできる

ex:

obj1 := Object(), obj
base := baseObject
obj2 := {base: baseObject}
obj3 := new baseObject
MsgBox % obj
foo " " obj2.foo " " obj3.foo

いつでもオブジェクトのベースを再割当てすることができ、オブジェクトのインジェクションが持つすべてのプロパティとメソッドを効果的に置き換えることができる