AppleScript でクロージャ

クロージャがなにかってことはイマイチよく分かっていないのだけど。

Python クックブック 第2版を読んでいて、載っていたスクリプトを AppleScript で書き直したら、動いたって話なんだけど。JavaScript ほど多彩なことができるわけではないのだけど。

まぁ、どこでどのように使うのかというギモンは残るんだけどね。

AppleScript の場合、関数(ハンドラ)の中に関数(ハンドラ)は作れない。これはご存知だと思います。しかし、スクリプトオブジェクトなら作ることができます。

Script Editor で開く

on makeObject(i)
    script MyObject
        property counter : i

        on countUp()
            set counter to counter + 1
        end countUp

        on getCounter()
            my counter
        end getCounter

        on run
            countUp()
        end run
    end script
end makeObject

set theObject to makeObject(0)

tell theObject
    run
    display dialog getCounter()
    run
    display dialog getCounter()
end tell

単純な例ですが、実行するたびにカウントを行うスクリプトオブジェクトです。makeObject ハンドラ実行時に引数を渡し、それが MyObject の属性に初期値として設定されています。これはこれで特に問題はないのですが、MyObject の属性 counter は書き換え可能です。

tell theObject
    run
    display dialog getCounter() -- 1
    run
    display dialog getCounter() -- 2
    set counter of it to 100 -- 値を書き換えてみる
    run
    display dialog getCounter() -- 101
end tell

スクリプトオブジェクトの属性は読み書き可能。これは、AppleScript では当たり前のことですね。では、次のように書き換えてみましょう。

Script Editor で開く

on makeObject()
    set counter to 0
    script MyObject
        on countUp()
            set counter to counter + 1
        end countUp

        on getCounter()
            return counter
        end getCounter

        on run
            countUp()
        end run
    end script
end makeObject

set theObject to makeObject()

tell theObject
    run
    display dialog getCounter() -- 1
    run
    display dialog getCounter() -- 2
    try
        set counter of it to 100 -- 値を書き換えてみる
    on error msg number num
        error number num
    end try
    run
    display dialog getCounter() -- 3
end tell

makeObject ハンドラ実行時の引数をなくし、MyObject の属性 counter を makeObject ハンドラ内のローカル変数にしてみました。ハンドラ実行時のローカル変数がハンドラ実行後も保持され、スクリプトオブジェクト内から参照可能です。が、スクリプトオブジェクトの外から counter の値を変更することも参照することもできません。

もう少し分かりやすく書くと以下のようになります。

Script Editor で開く

on DataObject(theKey, value)
    script
        on getKey()
            return theKey
        end getKey

        on getValue()
            return value
        end getValue
    end script
end DataObject

set theData to DataObject("id", "someone")
theData's getKey() -- "id"
theData's getValue() -- "someone"

変数 value や theKey にアクセスするにはハンドラを利用する以外ありません。もちろん、グローバル変数を利用すると話は別ですが(または、スクリプトオブジェクト内に setter を用意するとか)。

と、にハンドラ内のローカル変数が保持されることは分かったのですが、これがクロージャなのかといわれるとよくわからなかったりする。以下のようにしておけば、スクリプトオブジェクトの初期化処理を複数回呼び出すことを防いだりできるかな?

Script Editor で開く

on makeObject()
    set instance to missing value
    script MyObject
        on init()
            if instance is missing value then
                (* ここに初期化処理 *)
                -- ハンドラのローカル変数に自身を割り当てる
                set instance to me
            else
                -- 初期化されてるよ
                display dialog "Initialize OK"
            end if
        end init

        on greeting()
            display dialog "Hello"
        end greeting
    end script

    init() of MyObject
    return instance
end makeObject

set x to makeObject()
init() of x
greeting() of x

init ハンドラ自体を呼び出せないようにしたいけど、そこまで求めるのは無理があったりなかったり。

0 件のコメント :

コメントを投稿