インテリジェントでいこう

状況に応じた処理をしよう

 今まで説明してきたスクリプトは、記述内容を忠実に実行するものばかりでした。
 これでは、台本どおりにしか演じられない「アドリブの効かない役者」と同じです。
 台本どおりの処理なら、AppleScriptが備える「記録」機能を使えば簡単に作成できます(注1)。

 AppleScriptは全く融通の効かない役者ではありません。
 状況に応じて、適切な処理が可能です。
 とはいえ、人工知能内蔵で臨機応変に対応とまではいかないので、処理内容をあらかじめ台本(script)に記しておく必要があります。
 具体的には、「Aの場合はB」などといった「分岐処理」を記述します。
 これらの処理を重ねることで、さまざまな状況に対応したスクリプトを作成できます。

真偽値を使った分岐処理

Macも計算機なんだし」で、値を「true」もしくは「false」で示す真偽値(boolean)を紹介しました。
 この真偽値を結果として返す式は「条件式」と呼ばれ、分岐処理には欠かすことができません。
 分岐処理には、「if〜then」文を使い、「〜」の部分に条件式を書きます。

if 条件式 then
	trueの場合の処理
end if

 また、真偽両方の処理を行うには次の書式で条件分岐を行います。

if 条件式 then
	trueの場合の処理
else
	falseの場合の処理
end if

 次のスクリプトは、2分の1の確率で「あたり」か「はずれ」を表示します。

if (random number 1) = 0 then
	beep
	display dialog "あたり"
else
	display dialog "はずれ"
end if

「random number 1」は「0」か「1」を結果として返す命令です。
「0」の場合は警告音を鳴らして「あたり」を表示し、「1」の場合は警告音を鳴らさずに「はずれ」を表示します。

 さて、「else」を使って式を2つに分けてしまったら判定はもう終わり、と言うわけではありません。
「else」のあとに「if」を続ければ、さらに処理を分岐させて判定できます。

if 条件式1 then
	条件式1がtrueの場合の処理
else if 条件式2 then
	条件式1がfalseで条件式2がtrueの場合の処理
else if 条件式3 then
	条件式2がfalseで条件式3がtrueの場合の処理
    ・
    ・
else 
	すべての条件式がfalseの場合の処理
end if

 次のスクリプトは、連続した判定を利用したおみくじです。

set theNumber to random number 6
if theNumber = 0 then -- (注1)
	display dialog "大吉"
else if theNumber = 1 then
	display dialog "中吉"
else if theNumber = 6 then
	display dialog "凶"
else
	display dialog "吉"
end if

論理演算子を使って真偽値を分岐する

 真偽値は、「and」「or」「not」の論理演算子を使ってさらに複雑な条件を設定できます。
 and演算子は「かつ」を意味する演算子で、2つの真偽値の間に記述します。
 両方の真偽値が「true」の場合は「and」の結果は「true」となり、両方もしくは一方の真偽値が「false」の場合は「and」の結果は「false」となります。

true and false
	--> false

 or演算子は「または」を意味する演算子で、やはり2つの真偽値の間に記述します。
 一方の真偽値が「true」の場合は「or」の結果は「true」となり、両方の真偽値が「false」の場合は「or」の結果は「false」となります。

true or false
	--> true

 not演算子は「でない」を意味する演算子です。
「and」や「or」とは異なり、真偽値の直前に記述します。
 真偽値が「true」の場合は「not」の結果は「false」、真偽値が「false」の場合は「true」を返します。
 つまり「not」は真偽を逆にするわけです。

not true
	--> false

 コンピューターは複雑な処理を行っていますが、実はこれらの基本的な3つの論理演算子の組み合わせですべての処理を判定/実行しています。
 条件分岐が複雑になってくると何がなんだか分からない式が出来上がってしまいます。
 ()を使って条件をこまめに区切っていけば、長文でも処理内容を把握しやすくなります。

 次のスクリプトは、文字入力ダイアログに適当な数字を入力すると、「結果」ウィンドウに真偽値を返すプログラムです。
 入力した数字が10より大きく、50より小さければ「true」となります。

text returned of (display dialog "" default answer "")
(10 < result) and (result < 50) -- (注1)

 数学で「かつ」や「または」を概念として習った場合とは異なり、AppleScriptではスクリプトを実行して「かつ」や「または」の実行結果を確認できます。
 論理演算子に慣れるには、AppleScriptの文法が正しいかどうかを頭で考えるよりも、まずスクリプトを実行して仕組みを理解しましょう。

OSAX命令を使ってインタラクティブに

 OSAX命令の1つであるdisplay dialog命令の実行結果の値はレコードとなります。
 レコードは複数の属性を備えており、さまざまな値の入力に対応できます。OSAXはAppleScriptの機能を拡張するもので、display dialogは、StandardAdditions.osaxに含まれます。
 先ほどはtext returnedという属性を使いましたが、今度はクリックしたボタンを示す属性button returnedを使ってみましょう。

display dialog "どれがすき?" buttons {"Mac", "iPhone", "iPad"}
set theButton to button returned of result
if theButton = "Mac" then
	display dialog "キーボードがないとね!"
else if theButton = "iPhone" then
	display dialog "電話でないとね!"
else
	display dialog "持つにも読むにもいい大きさ!"
end if

 ここでは、theButtonという変数に結果を代入していますが、resultも使えます。
 ただし、resultは汎用的な変数のため、常に値が変化します。
 また、他の変数に比べてスクリプトの実行速度が遅くなります。
 ですから極力resultではなくtheButtonなどの自作の変数を使った方がいいでしょう(注1)。

 同じくOSAX命令のchoose file命令(注2)は、ファイル選択ダイアログを表示したあと、選択したファイルを結果に返します。
 次のスクリプトは、選択したファイルの「制作日」を返します。

set theFile to choose file "どのファイルを調べますか?"
tell application "Finder"
	set theDate to creation date of theFile
end tell
display dialog "制作日は" default answer theDate as string -- (注3)

 このスクリプトでは、値を返すためにdisplay dialogdefault answerオプションを利用しています。

 また、OSAX命令のchoose folder命令は、選択ダイアログを表示して、選んだフォルダを結果に返します。

set theFolder to choose folder "どのフォルダを調べますか?"
tell application "Finder"
	activate
	set theName to (name of theFolder) as string
	set the clipboard to theName
end tell

 このスクリプトでは、OSAX命令のset the clipbord to命令を使って、値をクリップボードに代入しています。
 結果は、[Command + V]キーでテキストエディタなどにペーストすることで確認できます。
 ただし、直前でactivate命令を実行しておかないとエラーが発生するため、tellブロックの間でset the clipboard to命令を使う場合は注意しましょう。tellブロックに挟まれていない場所では、activate命令を記述する必要はありません。

 アプリケーションによっても異なりますが、クリップボードの値を扱いたい場合はOSAX命令を使う、と理解しておけばいいでしょう。

全要素参照を使う

 全要素参照を使うと、すべての要素から特定のクラス要素をリストとして取り出せます。
 全要素参照の書式は次の通り(注1)。

every クラス名 of コンテナ

 コンテナに指定できる値には、文字列とリストがあります。文字列の場合は、クラス名によってコンテナを分割する単位が変わります。例えば次のスクリプトでは、「character」クラスを使って文字列を1文字ごとに分割しています。

every character of "文字ごとに分割"
	--> {"文", "字", "ご", "と", "に", "分", "割"}

 リストの場合は、要素の中から特定のクラスの値だけを取り出せます。次のスクリプトでは、リストの中から数値だけを抜き出して、「結果」ウィンドウに表示します。

every number of {2, "文字", 0.5, true}
	--> {2, 0.5}

 クラスを指定せずにすべての要素を取り出したい場合は、クラス名を「item」にします。
 全要素参照のコンテナには値だけではなく、要素を持ったオブジェクトへの「参照」も使えます。
 Finderのオブジェクトの場合は、「item」をはじめ「file」「folder」「disk」——などのクラス名が使えます(注2)。
 例えば次のスクリプトを実行すると、「home」フォルダ内にある全フォルダへの参照を「結果」ウィンドウに表示します。

tell application "Finder"
	every folder of home
end tell

selectionを使う

 フォルダの代わりにselectionをコンテナに指定することもできます。
 selectionは現在選択中の項目を表します。
 例えば、次のスクリプトでは選択しているファイルやフォルダのうちのひとつのコメントを変更します(注1)。

tell application "Finder"
	set theList to selection -- (注2)
	set comment of item 1 of theList to "新コメント"
end tell

 selectionを使ったアプレットをダブルクリックして起動すると、アプレット自身を対象にして処理を行ってしまいます。
 スクリプトメニューやサービスに登録するなどして使いましょう。

フィルタ参照を使う

 複数の属性を一括処理するには、whose文を使う手もあります。
 複数の参照に続けて「whose」を記述し、そのあとにオブジェクトの属性を使った条件式を作成すればOKです。
 これで、その条件に合ったオブジェクトだけを取り出せます。この処理を「フィルタ参照」と呼んでいます。

(全要素)参照 whose 条件式(属性を使う)

 次のスクリプトは、選択したフォルダの中で拡張子が"jpg"の書類をゴミ箱に移動します。

set theFolder to choose folder
tell application "Finder"
	delete (every file of theFolder whose name extension = "jpg")
end tell

 フィルタの条件には、前述の論理演算子を使って複数の条件が書けます。
 特に処理する範囲が広い場合、より絞り込むフィルタを書くと良いでしょう(注1)。

 このようにフィルタ参照は便利な機能ですが、Finderのようにアプリケーションがフィルタ参照に対応していないと使えません。
 アプリケーションがフィルタ参照に対応しているか、またどの程度まで対応しているかは、基本的には試してみないとわかりません。
 アプリケーションのサンプルにフィルタ参照を使ったスクリプトが含まれていたり、マニュアルにフィルタ参照に対応していると書いてあったらしめたものです。

【今回のまとめ】

AppleScriptは、

  • 条件式と「if〜then」で分岐処理を行うことが可能
  • 「else if」を繰り返せば、多くの条件を判定できる
  • 「and」と「or」で条件式をつなげられる

ユーザーとの対話には、

  • ダイアログボックスを使う
  • 「selection」を利用すると便利

複数の属性を一括処理する場合は、

  • 「every」を使って全要素参照が可能
  • フィルタ参照を使えば細かい参照を行える