同義表現から探るAppleScript

以下のスクリプトで、tellブロック内のclickで始まる6行は、すべて同じ操作を実行する。スクリプトメニューアイコンをクリックする操作だ。助詞や同義語を変更して書いてみた。それにしても、1行目と6行目ではずいぶん印象が違う。(と感じている。)


click_menu_extra("AppleScript")
on click_menu_extra(a_name) tell application "System Events"
tell process "SystemUIServer"
set frontmost to true
click (menu bar 1's first menu bar item whose attribute "AXDescription"'s value is a_name) click (menu bar 1's menu bar items whose attribute "AXDescription"'s value is a_name)'s item 1
click (menu bar 1's every menu bar item whose attribute "AXDescription"'s value is a_name)'s item 1
click item 1 of (every menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name) click (menu bar item 1 of menu bar 1 whose value of attribute "AXDescription" is a_name) click (first menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name) end tell
end tell
end click_menu_extra

  • 1行目は自分が一番好きな書き方。('sの連続)
  • 4行目は一般的なAppleScriptとして象徴される(と思う)書き方。(ofの連続)

それぞれの違いをもう少し詳しく調べてみると、注目すべき点は3つある。以下、抜き出してみた。


click_menu_extra("AppleScript")
on click_menu_extra(a_name) tell application "System Events"
tell process "SystemUIServer"
set frontmost to true
--click (menu bar 1's first menu bar item whose attribute "AXDescription"'s value is a_name)
--click (menu bar 1's menu bar items whose attribute "AXDescription"'s value is a_name)'s item 1
--click (menu bar 1's every menu bar item whose attribute "AXDescription"'s value is a_name)'s item 1
--click item 1 of (every menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name)
--click (menu bar item 1 of menu bar 1 whose value of attribute "AXDescription" is a_name)
--click (first menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name)
(* 'sか、ofか、 *)
menu bar 1's every menu bar item --'sトップダウン方式で指定する(日本の住所表記)
every menu bar item of menu bar 1 --ofボトムアップ方式で指定する(英語のアドレス表記)
(* 複数形か、everyか、 *)
menu bar items --メニューバーアイテムたち
every menu bar item --すべてのメニューバーアイテム
(* firstか、item 1か、 *)
menu bar 1's first menu bar item whose attribute "AXDescription"'s value is a_name
(menu bar 1's menu bar items whose attribute "AXDescription"'s value is a_name)'s item 1
menu bar 1's menu bar item 1 whose attribute "AXDescription"'s value is a_name
end tell
end tell
end click_menu_extra

  • 基本的にそれぞれ上側の表現が好みだった。のだが...
    • 今回、whoseフィルタ利用時でも「menu bar 1's menu bar item 1 whose attribute "AXDescription"'s value is a_name」でOKなことに、初めて気付いた。
    • できるだけ数字を使った方が直感的に頭で理解できるので、今後は「menu bar 1's menu bar item 1 whose ...」を常用しそう。
    • しかし、wohseの前が「menu bar 1's menu bar item 1」だと、多くの中からフィルタする感覚が抜けてしまい、違和感がある。
    • 悩みどころ。

AppleScriptは自然な英語表記でコーディングできることを目指しているらしい。自然言語は一つのことを多様な言い回しで表現できる。だから、AppleScriptにも上記のような複数の表現が生まれる。*1
しかし、自然言語的な曖昧さ加減が、ネイティブジャパニーズな自分には分かり難さ、間違い易さの原因になっていたりする。

  • 単数形と複数形の使い分けを間違う(first itemsは自動的にfirst itemに変換されてしまう。firstを削除して、そのまま気付かずにエラーが発生。)


click_menu_extra("AppleScript")
on click_menu_extra(a_name) tell application "System Events"
tell process "SystemUIServer"
set frontmost to true
click (menu bar 1's first menu bar item whose attribute "AXDescription"'s value is a_name) --OK
click (menu bar 1's menu bar item whose attribute "AXDescription"'s value is a_name)'s item 1 --NG
click (menu bar 1's menu bar items whose attribute "AXDescription"'s value is a_name)'s item 1 --OK
end tell
end tell
end click_menu_extra

  • 括弧の括り方を間違う。(どこまでが修飾される範囲か、英語そのものだと、分かり難くて。)


click_menu_extra("AppleScript")
on click_menu_extra(a_name) tell application "System Events"
tell process "SystemUIServer"
set frontmost to true
click item 1 of (every menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name) --OK
click (item 1 of every menu bar item of menu bar 1 whose value of attribute "AXDescription" is a_name) --NG
end tell
end tell
end click_menu_extra

リスト

  • リストを取り扱う時の多様な表現の例(同義でない表現も含めて)


{1, 2, 3, 4, 5}'s item 1 --結果 1

{1, 2, 3, 4, 5}'s some item --結果 ?1から5がランダムに選択される)
{1, 2, 3, 4, 5}'s every item --結果 {1, 2, 3, 4, 5}(すべてのアイテムが選択される)
{1, 2, 3, 4, 5}'s items --結果 {1, 2, 3, 4, 5}(すべてのアイテムが選択される)

{1, 2, 3, 4, 5}'s first item --結果 1
{1, 2, 3, 4, 5}'s second item --結果 2
{1, 2, 3, 4, 5}'s third item --結果 3
{1, 2, 3, 4, 5}'s fifth item --結果 5

{1, 2, 3, 4, 5}'s 1st item --結果 1
{1, 2, 3, 4, 5}'s 2nd item --結果 2
{1, 2, 3, 4, 5}'s 3rd item --結果 3
{1, 2, 3, 4, 5}'s 5th item --結果 5

{1, 2, 3, 4, 5}'s middle item --結果 3(真ん中のアイテム)
{1, 2, 3, 4, 5, 6}'s middle item --結果 3(真ん中のアイテム)

{1, 2, 3, 4, 5}'s last item --結果 5
{1, 2, 3, 4, 5}'s -1th item --結果 5
{1, 2, 3, 4, 5}'s item -1 --結果 5

{1, 2, 3, 4, 5}'s rest --結果 {2, 3, 4, 5}(先頭以外の残り)
{1, 2, 3, 4, 5}'s reverse --結果 {5, 4, 3, 2, 1}(逆順に並べ替える)

{5, 4, 3, 2, 1}'s number --結果 5(リストアイテムの数)
count {5, 4, 3, 2, 1} --結果 5(リストアイテムを数える)



tell application "Finder"
(* プロパティを持つオブジェクトのリストの場合 *)
window 1 --インデックス指定
window "/" --名前指定
window id 991 --ID指定
windows whose class is Finder window --プロパティ条件で絞り込み、ウィンドウをリストで返す
windows's properties --全てのウィンドウのプロパティをリストで返す
some window --ランダムに選択してウィンドウを1つ返す
every window --全てのウィンドウのリストを返す
first window
second window
third window
fifth window
1st window
2nd window
3rd window
5th window
middle window
last window
-1th window
end tell

meとmy


(* AppleScript エディタで実行した場合 *)
name of me --OK--結果 "AppleScript Editor"
my name --OK--結果 "AppleScript Editor"
me's name --OK--結果 "AppleScript Editor"

path to me --OK--結果 alias "Leopard HD:Applications:Utilities:AppleScript Editor.app:"
my path --NG--結果 error "path を取り出すことはできません。" number -1728 from path
me's path --NG--結果 error "path を取り出すことはできません。" number -1728 from path

  • 基本的にmyをよく使うが、meでないと表現できないこともある。

is in と contains


"apple" is in {"orange", "apple", "remon"} --結果 ture("apple"がリストの中にあるかどうか)
{"orange", "apple", "remon"} contains "apple" --結果 true(リストに"apple"が含まれるかどうか)

"apple" is in "This is AppleScript" -- 結果 ture("apple"が存在するか--部分一致)
"This is AppleScript" contains "apple" --結果 ture("apple"を含むか--部分一致)
"This is AppleScript" begins with "this" --結果 ture("this"で始まるか--前方一致)
"This is AppleScript" ends with "script" --結果 ture("script"で終わるか--後方一致)

  • is in をよく使う。(is、is notと比較対象アイテムの語順が同じになるので)
  • 但し、テキストの部分・前方・後方一致を調べる時は、containsを使いたくなるかもしれない。

ハンドラのラベル付き引数

ハンドラの引数は、括弧()で括る以外に、前置詞やラベルを含めた自然な英語風に定義可能。


on replacedText1(originalText, newText, oldText) return {originalText, newText, oldText} end replacedText1

on replacedText2 of originalText by newText instead of oldText
return {originalText, newText, oldText} end replacedText2

on replacedText3(originalText, {new:newText, old:oldText}) return {originalText, newText, oldText} end replacedText3

on replacedText4 of originalText given new:newText, old:oldText
return {originalText, newText, oldText} end replacedText4

replacedText1("apple", "z", "a") --置換操作が"z""a"か、"a""z"か、曖昧さが残る
replacedText2 of "apple" by "z" instead of "a" --日本人には直感的に理解し難い
replacedText3("apple", {old:"a", new:"z"}) --ラベルによって、置換操作が明確になる
replacedText4 of "apple" given old:"a", new:"z" --ハンドラの引数がどこまで続くか、可読性が悪い

  • ネイティブ英語圏の人達には、自然な英語風のラベル付きのハンドラ定義は直感的で分かり易いのかもしれないが、
  • 生粋の日本人である自分にとっては、直感的に解釈できない、可読性の悪いスクリプトになってしまう...。
  • シンプルに、replacedText1()か、replacedText3()を常用したい。
  • 利用可能な定義済ラベル(前置詞)
    • about, above, against, apart from, around, aside from, at, below, beneath, beside, between, by, for, from, instead of, into, on, onto, out of, over, since, thru (or through), under
givenで与えられたラベル付き真偽値を含むハンドラの取り扱い


--givenで与えられたラベル付き真偽値は、withwithoutに自動変換される(上のコメント表現が、下のコードに自動変換される)
to findNumbers of numberList above minLimit given rounding:roundBoolean
set resultList to {} repeat with i from 1 to (count items of numberList) set x to item i of numberList
if roundBoolean then -- round the number
-- Use copy so original list isn’t modified.
copy (round x) to x
end if
if x > minLimit then
set end of resultList to x
end if
end repeat
return resultList
end findNumbers

--findNumbers of {2, 5, 19.75, 99, 1} above 19 given rounding:true
findNumbers of {2, 5, 19.75, 99, 1} above 19 with rounding --結果 {20, 99}

--findNumbers of {2, 5, 19.75, 99, 1} above 19 given rounding:false
findNumbers of {2, 5, 19.75, 99, 1} above 19 without rounding --結果 {19.75, 99}



--givenで与えられた引数の様々な変換例
to testNumbers of numberList above minLimit given rounding:roundBoolean, test1:val1, test2:val2
--処理省略--
return {numberList, minLimit, roundBoolean, val1, val2} end testNumbers

--testNumbers of {2, 5, 19.75, 99, 1} above 19 given rounding:true, test2:"efg", test1:"abc"
testNumbers of {2, 5, 19.75, 99, 1} above 19 with rounding given test2:"efg", test1:"abc"

--testNumbers of {2, 5, 19.75, 99, 1} above 19 given rounding:true, test2:true, test1:true
testNumbers of {2, 5, 19.75, 99, 1} above 19 with rounding, test2 and test1

--testNumbers of {2, 5, 19.75, 99, 1} above 19 given rounding:false, test2:true, test1:true
testNumbers of {2, 5, 19.75, 99, 1} above 19 with test2 and test1 without rounding

  • ここまで、自然な英語にこだわるAppleScriptは凄いのだけど、自分は括弧で定義したい。
  • 自然な英語風よりも、数学の証明風なコード表現の方が好き。

起動

  • launch・run・activateの微妙な違い。


tell application "TextEdit" to launch --起動のみ
tell application "TextEdit" to run --起動+"名称未設定"書類作成+隠された状態
tell application "TextEdit" to activate --起動+"名称未設定"書類作成+最前面に表示

作業環境

*1:どのプログラミング言語でも、多かれ少なかれ複数の言い回しはあるものだが、AppleScriptについては特に多い気がした。