seraphyの日記

日記というよりは過去を振り返るときのための単なる備忘録

AppleScriptを使ってみるよ!(言語編2/2)

前回は言語編1/2として変数やスクリプトまわりを調べてみたよ。
今回はAppleScript特有に見えるオブジェクトの選択・指定方法について調べてみるよ。


(再び) 文字列を使ってみるよ。


ActionScriptではほとんどのオブジェクトは0個以上の要素(Elements)をもてるよ。
その意味は、たとえばリストを例にとれば明白であるが、リストというオブジェクトの中には0個以上の要素がある、という意味だよ。


文字列とはtext型のオブジェクトのことであるが、リストと同様に0個以上の「文字」を要素(Elements)とするよ。

文字列は文字を要素とみるだけでなく、段落を要素とみることも、単語を要素とみることもできるよ。

set mes to "Hello, World!!" & return & "こんにちは、世界"
log every character of mes -- 文字を要素としてみる場合
log every word of mes -- 単語を要素とみる場合
log every paragraph of mes -- 段落を要素とみる場合
log text of mes -- 文字列を要素とみる場合(つまり、そのまま。)

結果は、こんな感じだよ。

(*H, e, l, l, o, ,,  , W, o, r, l, d, !, !, 
, こ, ん, に, ち, は, 、, 世, 界*)
(*Hello, World, こんにちは, 世界*)
(*Hello, World!!, こんにちは、世界*)
(*Hello, World!!
こんにちは、世界*)

paragraphは改行(return)で分割されるのは自然として、wordは英語圏であれば空白またはカンマなどの区切り記号により分割され単語でバラにされるようだけれど、日本語の単語の分割は当てにならない気がするよ。


every character, every wordのかわりに、characters, wordsとしても良いよ。(省略記法かな?)


参照形式


このeveryというのがAppleScriptの参照形式(Reference Form)というもので、オブジェクトあるいはオブジェクトのグループを特定するための指示句のようだよ。
いままで何気に使っていた「index n」というのもオブジェクトを特定するための指示句だったんだね。

参照形式には以下のものがあるよ。

  • Arbitrary おまかせの一品を選ぶ
  • Every 全部選ぶ
  • Filter 条件に合致するものを選択
  • ID 等しいIDプロパティを選択
  • Index 位置指定で選択
  • Middle 中間位置で選択、滅多に使われない
  • Name 等しいNameプロパティで選択
  • Property ラベルのものを選択
  • Range 範囲を指定して一連のものを選択
  • Relative 相対指定、対象の前後を選択
Arbitrary 任意参照形式

文法は「some 対象」という形で指定するよ。対象が複数あれば、そのうち、どれかが(ランダムに)選択されるよ。

set lst to {1, 2, 3, 4, 5}
repeat 3 times
	log some item of lst
end repeat

結果は、こんな感じだよ。

(*5*)
(*1*)
(*2*)
every 全選択

全選択は取り立てて説明することはないよね。
選択したものをリストとして返すよ。
選択するものが何もなければ空のリストが返されるよ。

set lst to {1, 2, 3, 4, 5}
log every item of lst -- {1, 2, 3, 4, 5} なにも変わらず。
Filter 条件選択

条件に合致するものを選択してリストとして返すよ。
該当が一つもなければ空のリストが返されるよ。


文法は「対象指定子 (whose | where) 条件式」の形式で指定するよ。


便利そうだけどサポートしているのはアプリケーションオブジェクトのみで、リストやレコード、テキストなどのオブジェクトには使えないよ。残念。


ためしに、iTunesのトラックから☆が1つ以上のものを出してみるよ。

tell application "iTunes"
	set tks to every file track whose rating > 0 -- ☆ひとつで20、☆5ならば100
	repeat with tk in tks
		log ((name of tk) as text) & ":" & (id of tk) as text
	end repeat
end tell

「whose rating > 0」でratingが0より大きなものを選択しているよ。
結果はいっぱいあるので一部抜粋。

	get name of file track id 6249 of library playlist id 5560 of source id 42
		--> "想いを奏でて"
	get id of file track id 6249 of library playlist id 5560 of source id 42
		--> 6249
	(*想いを奏でて:6249*)
	get name of file track id 6248 of library playlist id 5560 of source id 42
		--> "いつか溶ける涙"
	get id of file track id 6248 of library playlist id 5560 of source id 42
		--> 6248
	(*いつか溶ける涙:6248*)
	get name of file track id 5733 of library playlist id 5560 of source id 42
		--> "海のオパール"
	get id of file track id 5733 of library playlist id 5560 of source id 42
		--> 5733
	(*海のオパール:5733*)
ID選択

IDが合致するものを選択する参照形式だよ。
またしても、IDプロパティをサポートするアプリケーションオブジェクト専用だよ。


前回のFilterのテストでidを表示させたので、これで検索してみるよ。

tell application "iTunes"
	set tk to file track id 6248 -- 前回の結果からコピー
	log (name of tk) as text
end tell

結果

tell application "iTunes"
	get file track id 6248
		--> file track id 6248 of library playlist id 5560 of source id 42
	get name of file track id 6248 of library playlist id 5560 of source id 42
		--> "いつか溶ける涙"
	(*いつか溶ける涙*)
end tell

同じアイテムが取れていることが確認できるよ。
(このidの生存期間がよくわからないけれどね。iTunesのマニュアルは、まだ確認してないのでわからない。)

Index 位置指定

リストなどの始まりと終わりのあるようなコンテナで使える位置指定参照だよ。


記述方法もいろいろあるよ。

set lst to {1, 2, 3, 4, 5}
log item index 1 of lst -- 1 (indexは省略可)
log item 1 of lst -- 1
log first item of lst -- 1
log 1st item of lst -- 1
log front item of lst -- 1
log last item of lst -- 5
log back item of lst --5 


インデックスを負の値にすると、後ろからの順序での指定になるよ。

set lst to {1, 2, 3, 4, 5}
log item -1 of lst -- 5
log item -2 of lst -- 4
Middle 中間位置

中間位置の参照形式で、index参照形式が使える場合のにみ使えるよ。

log middle item of {1, 2, 3, 4, 5} -- 3
log middle item of {1, 2, 3, 4} -- 2
log middle item of {1, 2, 3} -- 2
log middle item of {1, 2} -- 1
log middle item of {1}
try
	log middle item of {} -- エラーになるよ
end try
Name 名前指定参照形式

名前が等しいオブジェクトを参照する形式だよ。マニュアルにはそうとは書かれていないけれど、IDと同様にアプリケーションオブジェクト専用っぽいよ。

tell application named "iTunes" to pause -- namedは省略可能
tell application "iTunes" to play

この場合、あまたのappliationの中から「iTunes」という名前のアプリケーションを選択している、ということらしいよ。

Property プロパティ指定参照形式

プロパティ名を指定してオブジェクトを参照する形式だよ。
こちらは、ユーザ定義のプロパティでも問題ないよ。

log P1 of {P1:"abc", P2:"def"} -- abc
Range 範囲指定参照形式

indexで位置指定したものの範囲版みたいだよ。
from .. to で指定するか、もしくは、thruで指定するよ。

set lst to {1, 2, 3, 4, 5}
log every item from 2 to 3 of lst -- items 1 thru 2 でも可
log items 2 thru 3 of lst -- {2, 3}となる
log items 2 as list -- 2

範囲指定には取り出す型も指定できるよ。
その場合、その型でフィルタしたものの順序で範囲指定するよ。

set lst to {1, "a", 2.5, false, true, 3.0, "b", 2, 3, "c"}
log item 1 of lst -- 1
log items 1 thru 2 of lst -- 1, "a"
log integer 2 of lst -- 2
log integers 1 thru 2 of lst -- 1, 2
log text 2 of lst -- b
log text 1 thru 2 of lst -- a, b
log real 2 of lst -- 3.0
log reals 1 thru 2 of lst -- 2.5, 3.0
log boolean 1 of lst -- false
log booleans 1 thru 2 of lst -- false, true
Relative 相対位置指定参照形式

すでに位置指定されているものの相対位置で指定する参照形式だよ。
しかしながら、リストの先頭または末尾への挿入位置を示す「beginning/front」, 「end/back」の参照形式も「相対位置指定」と言われているよ。

set lst to {}
set end of lst to "z"
set beginning of lst to "a"
log lst -- {a, z}

before/afterなどの指定方法は、いまいち使いどころが不明且つ理解できないので省略。あとで必要になったら調べるよ。


オブジェクトの選択方法を調べてみるよ。


ここまでくると、ようやくAppleScriptのイベントと、その送信先の指定についての仕組みが分かってきたような気がするよ。
ちょっと順序が逆のような気もするけれど、イベントの送信先の指定方法について調べるよ。

of/inを使う方法


オーソドックスというか、これが基本形だと想うよ。

いままでofばかり使っていたけれど、inも同じ意味になるらしいよ。

あと、A'sのように「's」と書いてもいいらしいよ。

log item 1 of {1, 2, 3, 4, 5}
log item 1 in {1, 2, 3, 4, 5} -- inもofと同じ

log A of {A:"abc"}
log A in {A:"abc"}
log  {A:"abc"}'s A -- X of Yは、Y's X と書ける
tellを使う方法


アプリケーション相手にする場合は、これが基本形になると思うよ。

でも、オブジェクトの送信先を指定するという役割でしかないので、基本的にはof/inを一括指定するためのものと考えて良いと思うよ。

(コンパイル時の文法チェックが及ぶ範囲指定でもある。それは後述。)

(* 単文 *)
tell {1, 2, 3} to item 1 -- item 1 of {1, 2, 3} と等しい

(* 複文 *)
local x, y, z
tell {1, 2, 3}
	set x to item 1 -- {1, 2, 3}'s item 1 と同等
	set y to length -- {1, 2, 3}'s length と同等
	set z to every item -- {1, 2, 3}'s every item と同等
end tell
log x -- 1
log y -- 3
log z -- {1, 2, 3}

複文のtellでは、明示的に送信先オブジェクトを指定しない場合、tellで指定したオブジェクトが暗黙で使われるよ。

他のオブジェクトを送信先にしたい場合は新たにtellを使うか、あるいは、単にof/inで個別に明示すれば良いよ。


meおよびitオブジェクト


meは現在のトップレベスクリプトを示す送信先で、
itは現在の暗黙の送信先を意味するキーワードだよ。

説明するまでもないね。


resultキーワード


meやitとは性質が違うが、同じような暗黙のオブジェクトとしてresultがあるよ。

resultはイベントの実行後に、その結果が暗黙でセットされる「最後の実行結果」が格納されるキーワードだよ。

なので、戻り値を参照したいだけであれば、あえて別の一時変数に格納するような手間を省けるよ。

(* resultの使用例 *)
display dialog "選択せよ" buttons {"TRUE", "FALSE"}
if result's button returned = "TRUE" then log "TRUE"

(* 明示的に保存する場合 *)
set ret to display dialog "選択せよ" buttons {"TRUE", "FALSE"}
if ret's button returned = "TRUE" then log "TRUE"

AppleScriptコンパイル言語だよ。


AppleScriptスクリプトというけれど動的言語に多く見られる実行時バインドのような柔軟性はなく、むしろ、ガチガチに固められたコンパイル言語っぽいよ。

送信先のオブジェクトを指定するとき、それが、どのようなプロパティやエレメント、あるいはハンドラをもっているかを事前に知らないとコンパイルできないよ。

たとえば、

tell application "iTunes"
	set tks to every file track whose name starts with "f" -- fで始まる曲名を選択
end tell

は、iTunesの文脈では合法なのでコンパイルできる。しかし、

tell application "TextEdit" -- 見当違いな送信先
	set tks to every file track whose name starts with "f" -- エラー
end tell

なんてすると、そもそもコンパイルできなくなるよ。
(あるいは別のターゲットオブジェクトに、たまたま同じハンドラ等があればコンパイルできるけれど実行時にエラーになる可能性が高いよ。)

TextEditには使えないってことが、コンパイラには分かるんだね。


つまり、コンパイラは、applicationという指定から、そのアプリにとって合法であるかチェックしているということだよ。


そうすると、動的にアプリケーション名を指定したいとか、tellの中から呼び出される外のハンドラでアプリケーションのオブジェクトを使うためには、どうしたらよいか、ってことになるね。


そのためのブロックが「using terms from xxxx … end」ブロックだよ。

display dialog "アプリケーション名を指定" default answer "iTunes"
set appName to result's text returned
log appName

using terms from application "iTunes" -- iTunesオブジェクトでコンパイルする
	tell application appName -- 実際のアプリはコンパイラには分からない。
		set tks to every file track whose name starts with "f"
		repeat with tk in tks
			print_tk(tk) of me
		end repeat
	end tell
end using terms from

to print_tk(tk) -- iTunesのtrackオブジェクトを渡すことを想定
	using terms from application "iTunes" -- iTunesオブジェクトでコンパイルする
		tell tk
			log (album as text) & tab & (name as text) & tab & (duration as number)
		end tell
	end using terms from
end print_tk

実行するアプリケーションがリモートマシン上にあるようなケースでも同じことがいえるね。
その場合は、tellはリモートマシン上のアプリケーションを指定するものの、コンパイル時はローカルマシン上の辞書を用いてコンパイルすることになるよ。

終わり


とりあえずAppleScriptソースコードを読むためのおおざっぱな文法知識はえられたんじゃないかな、と思うよ。
ソースコードから言語仕様上こうだから、こう動くはず、と断定できるほどの知識はないけいれど、そこは都度調べればなんとかなるんじゃないかな。

次はAppleScriptに標準で備わっているコマンド群について調べてみるよ。