Макинтош и образование:Макинтош:AppleScript — это совсем не страшно:Do you speak AppleScript? |
Это — вторая статья цикла. Вы уже немного знакомы с тем, как пишутся скрипты, а может быть — и я на это надеюсь — попробовали что-то писать самостоятельно. Но те несколько конструкций языка, что были использованы в нашем первом занятии,- лишь малая его часть. Сегодня мы расширим наш инструментарий. Конечно, журнальная статья не сможет заменить четырехсотстраничный справочник по языку (AppleScript Language Guide). Так что воспринимайте ее как карманный словарик-разговорник.
Прежде всего договоримся об используемых терминах. Как Вы уже знаете, основное назначение AppleScript — управление объектами (object) в прикладных программах (окнами, элементами данных и т. п.). Объект может включать другие объекты — его элементы (element). Например, текст состоит из абзацев, слов и символов. Любой объект является представителем («экземпляром») какого-либо класса объектов (class). Все экземпляры класса имеют один и тот же набор свойств (property), состоят из однотипных элементов, для всех их определена реакция на одинаковые команды (в объектно-ориентированных языках их обычно называют методами; будем и мы использовать этот термин).
Имя | Описание | Пример |
---|---|---|
Copy…to… | Копирование значения выражения в переменную (переменные) | copy "Article" to myWork |
Count…in… | Подсчет количества элементов | count integers in {"Yes", 2, 10, 3.5} дает результат 2 |
Get… | Вычисление значения выражения. Слово Get можно опускать | get 2*2 дает результат 4 |
Run… | Исполняет объект, не являющийся выражением | |
Set…to… | Присваивает переменной (переменным) значение выражения | set width to 158 |
В AppleScript существует механизм «общих данных» (Data Sharing). Т. е. несколько переменных (списков, записей или объектов скрипта) обращаются фактически к одной и той же области памяти. Например, записав
set SecondList to FirstList
мы создаем не новый список, а только второе имя для уже существующего. Соответственно, любое изменение в FirstList будет отражаться на SecondList и наоборот. Если мы хотим создать на самом деле новый список, только вначале совпадающий с другим, необходимо использовать метод Copy.
Для простых переменных методы Copy и Set взаимозаменяемы.
Однако, для написания скрипта только объектов и их методов недостаточно. Нужно связать эти слова в «предложения», определяющие ход выполнения программы,- операторы. Они бывают простыми — однострочными или составными — занимающими несколько строк и содержащими другие операторы (вспомните, например, «tell…end tell» или «if…then…end if»).
Классы и методы прикладной программы — это ее словарь. Все слова из него, а также из стандартного набора операторов AppleScript называют зарезервированными. Когда мы создаем собственные свойства, методы или переменные в скрипте, называть их именами, совпадающими с резервированными словами, нельзя. А вообще имена, как и во многих других языках, могут состоять из латинских букв, цифр и символа подчеркивания (_), причем первым символом обязательно должна быть буква. Различать здесь заглавные и строчные буквы AppleScript не умеет.
Чтобы человеку (например, Вам самим) было легче разобраться с программой, не помешает вставить в нее пояснения — комментарии. В AppleScript это можно сделать двумя способами:
(* Очень-очень длинный комментарий *) set width to 100 -- а это комментарий к строке
AppleScript может работать с данными различных типов, как простыми, так и структурированными. Набор простых данных довольно обширен.
Имя | Описание | Пример |
---|---|---|
Boolean | Логическое значение | false, true |
Date | Дата (строка, включающая день недели, число, месяц, год и время) | "Среда, 9 января 2002 23:20:59" |
Integer | Целое | 3 |
Real | Вещественное число | 3.0 |
Reference | Ссылка на объект | window "Неименованный" |
String | Строка (Последовательность символов) | "Величина" |
Styled Text | Стилизованный текст (содержащий информацию о шрифте) | |
Text | синоним класса string |
Значения можно преобразовывать из одного типа в другой, используя операцию «as». Например, "123" as integer превратит исходную строку в число 123. Зачем? — Хоть бы для того, чтобы произвести с ним какие-либо вычисления. Если же мы захотим вставить результат расчетов в текст — придется наоборот преобразовать число в строку.
Структурированные переменные хранят сразу несколько значений. В AppleScript это данные типа список (list) и запись (record).
Список записывается как заключенная в фигурные скобки последовательность значений любых типов, перечисленных через запятую. Например: set MyList to {25, 324, "документ", 13}. Конкретный элемент может быть получен по своему порядковому номеру. В нашем случае, item 2 of MyList даст число 324.
Элементы записи — поля (field) — также записываются в фигурных скобках, но их порядок роли не играет, зато каждый имеет собственную уникальную метку. По этой-то метке мы и будем к нему обращаться. Например, задать всю запись целиком можно вот так: set MyData to {name: "Михаил", height: 180, weight: 80}. А получить значение одного поля — так: name of MyData.
Значение величины может быть либо задано в явном виде в самом скрипте{сноска: последовательность знаков, явно указывающую значение, называют литералом}, как в использованных выше примерах, либо получено как результат метода, либо — вычислено путем выполнения тех или иных операций над уже имеющимися значениями. Многие операции в AppleScript могут записываться несколькими разными способами, в таблице приведено по одному (обычно, наиболее короткому) варианту.
Обозначение | Описание | Операнды | Результат |
---|---|---|---|
and | И | Boolean | Boolean |
or | Или | Boolean | Boolean |
not | Не | Boolean | Boolean |
= | равно? | любой тип | Boolean |
Is Not | не равно? | любой тип | Boolean |
> | больше? | любой тип | Boolean |
< | меньше? | любой тип | Boolean |
>= | больше либо равно? | любой тип | Boolean |
<= | меньше либо равно? | любой тип | Boolean |
Starts With | начинается с? | список или строка | Boolean |
Ends With | заканчивается на? | список или строка | Boolean |
Contains | содержит? | список, запись или строка | Boolean |
Does Not Contain | не содержит? | список, запись или строка | Boolean |
Is In | содержится? | список, запись или строка | Boolean |
Is Not In | не содержится? | список, запись или строка | Boolean |
& | конкатенация (соединение) | строки | строка |
записи | запись | ||
любые другие типы | список | ||
* | умножение | число | число |
+ | сложение | число, Date | число, Date |
- | вычитание | число, Date | число, Date |
/ | деление | число | Real |
mod | деление нацело | Integer | Integer |
div | остаток | Integer | Integer |
^ | возведение в степень | число | Real |
Если требуется преобразовать какую-либо величину в значение другого типа, используют операцию приведения типа As. Например:
set MyResult to 132 / 11 as string -- результатом будет строка "12.0"
При работе с датами можно прибавлять к ним (и вычитать из них) не только значения того же типа, но и целые числа, которые при этом воспринимаются как промежуток времени в секундах. Для удобства предусмотрены стандартные константы: minutes, hours, days и weeks, равные соответствующему числу секунд (60, 3600 и т. д.).
В простейшем случае программа на AppleScript может выглядеть как последовательность поочередно вызываемых методов, в каждом из которых указывается полная (т. е. однозначно определяющая объект среди всех, имеющихся на компьютере) ссылка. Однако, для практического использования это, чаще всего, не подходит. Нужно иметь возможность повторять одинаковые действия с различными объектами, выбирать те или иные варианты действий, адекватно реагировать на возникающие ошибки, да и ссылки хотелось бы записывать, по возможности, короче. Для решения таких проблем и служат управляющие операторы. Начнем с уже известных вам.
Оператор Tell указывает объект, к которому относятся все заключенные в нем операторы (кроме тех, где использована полная ссылка). Записывается он, как Вы, вероятно, помните, вот так{сноска: запись «[оператор]…» обозначает «любое количество операторов»}:
tell ссылка [оператор]... end tell
tell window 1 of application "Tex-Edit Plus" insert date set selection to "\r" -- \r обозначает перевод строки insert time end tell
Если к данному объекту относится только один оператор, запись можно сократить:
tell ссылка to оператор
tell application "Finder" to set diskSpace to free space of disk "Macintosh HD" tell window 1 of application "Tex-Edit Plus" to ¬ set selection to "На диске свободно " & diskSpace div (1024 * 1024) & " Мбайт"
В AppleScript есть, как и в любом другом языке, средства для многократного повторения тех или иных действий. Вариантов циклов — масса, на все случаи жизни. Но это вовсе не значит, что придется помнить десятки английских слов.
В простейшем случае оператор цикла записывается вот так:
repeat [оператор]... end repeat
Здесь не указано, как компьютеру определить, что пора заканчивать повторения. Цикл будет бесконечным. Конечно, в реальной программе его надо будет рано или поздно прервать. Для этого есть оператор
exit -- «выход из цикла»
Чаще условие окончания цикла записывают непосредственно в его заголовке.
repeat while логическое выражение -- цикл «Пока» [оператор]... end repeat
или
repeat until логическое выражение -- цикл «До» [оператор]... end repeat
Единственное отличие между ними в том, что первый выполняется пока логическое выражение истинно, а второй — наоборот, заканчивается, когда оно станет истинным.
Зачастую, число повторов нам известно заранее, тогда можно сразу так и написать:
repeat целое times -- «повторить N раз» [оператор]... end repeat
А можно указать, что специальная переменная (параметр цикла) должна поочередно принять все значения из заданного диапазона. С каждым из них выполнится тело цикла. Сколько значений — столько и повторений.
repeat with переменная from целое to целое [оператор]... end repeat
Вообще-то, параметр может принимать и не все значения, то есть изменяться не на единицу, а на какую-либо другую (но обязательно целую!) величину — шаг:
repeat with переменная from целое to целое by целое [оператор]... end repeat
Наконец, мы можем заставить параметр перебирать элементы любого заданного списка.
repeat with переменная in список [оператор]... end repeat
Обратите внимание: при работе такого цикла параметр только ссылается на элемент списка. А для получения значения этого элемента нужно использовать операцию «contents» (содержимое), как в следующем примере.
tell application "Tex-Edit Plus" tell window 1 set parNum to 1 -- счетчик абзацев set y to paragraphs -- список всех абзацев текста repeat with par in y set paragraph parNum to (parNum as string) & ¬ " " & contents of par -- перед абзацем вставляем его номер set parNum to parNum + 1 end repeat end tell end tell
Еще одна классическая программная структура — выбор. С простейшим ее вариантом, позволяющим компьютеру решить, надо ли выполнить какое-либо действие, Вы уже знакомы по первой статье цикла:
if логическое выражение then [оператор]... end if
На практике ничуть не реже применяют «полную» форму условного оператора:
if логическое выражение then [оператор]... -- выполнить при истинности условия else [оператор]... -- а так сделать, когда условие — ложное end if
За счет вложенных «if» можно выбирать и более чем из двух разных вариантов. Например:
if x > y then display dialog "Перелет!" else if x < y then display dialog "Недолет!" else display dialog "Попал!!!" end if
К сожалению, далеко не все и не всегда происходит так, как мы бы хотели. Наш скрипт вполне может столкнуться с «исключительной ситуацией». Например, не оказалось файла, который он должен был бы открыть, или скрипт пытается изменить свойства несуществующего объекта, или… Да всего не перескажешь. Кстати, некоторые «ошибки» появляются и при нормальном выполнении операций. К примеру, если пользователь в диалоге «Open File» щелкнет «Cancel». Так вот, если не принять специальных мер, во всех таких ситуациях скрипт будет просто прерывать свою работу. А это — не слишком хорошо. Поэтому в AppleScript предусмотрен особый управляющий оператор:
try [оператор]... -- «нормальные» операции on error ¬ [переменная-строка] ¬ [number переменная-целая] ¬ [from переменная-ссылка] ¬ [partial result переменная-список] ¬ [to переменная-класс] [global переменная [, переменная]... ] [local переменная [, переменная]... ] [оператор]... end try
Он приказывает компьютеру попытаться выполнить некоторые действия (описанные в первой его части), а если попытка заканчивается неудачей — перейти к обработке полученного кода ошибки. При этом обработчик получает исчерпывающую информацию об ошибке:
Вот пример обработки «ошибки», когда пользователь отказывается от выбора файла:
try choose file set fileName to result on error errText display dialog "Ошибка: " & errText & ¬ "\r Использовать стандартные настройки?" ¬ buttons {"Завершить", "Стандарт"} default button 2 if button returned of result = "Завершить" error number -128 -- прерывание скрипта else set fileName to defaultFileName end if end try
Обратите внимание: здесь использован еще один новый для Вас оператор — «error». Он позволяет сигнализировать об ошибке, причем мы можем использовать как стандартные коды ошибок MacOS или AppleScript, так и свои собственные. Почему бы, например, не написать строку-сообщение по-русски?
А вот как можно использовать параметры «from» и «to»:
display dialog "Введите число:" default answer "" try set number1 to text returned of result as number -- display dialog возвращает результат типа -- запись, мы берем из нее только введенный -- пользователем текст и пытаемся преобразовать -- его в число on error from errObj to errClass display dialog "Введено «" & errObj & ¬ "», когда ожидались данные типа " & errClass set number1 to 0 end try
Как Вы, надеюсь, помните, реакция объекта на то или иное событие описывается подпрограммой-обработчиком (handler). В простейшем случае они оформляются вот так:
on событие ([параметр[, параметр]...]) [оператор]... end событие
Вот, например, как можно осуществить поиск наименьшего числа в заданном списке:
on minimum(numList) local min -- возьмем первое число set min to item 1 of numList -- и просмотрим все элементы списка repeat with numRef in numList set num to contents of numRef -- если очередное число меньше, -- берем его за минимум if num < min then set min to num end repeat return min end minimum
Обратите внимание: если обработчик должен возвращать какой-либо результат, это делается с помощью оператора Return. Таких операторов может быть и несколько, выполнение любого из них будет завершать работу обработчика. (Замечу, что можно обойтись и без Return. В этом случае будет возвращен результат последнего исполненного оператора. Но я не рекомендую так делать — если не хотите лишних проблем в процессе отладки скриптов)
Иногда разработчику скрипта бывает удобнее вместо упорядоченной записи параметров воспользоваться метками (в результате вызов обработчика становится более похожим на естественную английскую фразу). Оформляется подпрограмма с помеченными параметрами следующим образом:
on событие ¬ [ переменная-параметр ] ¬ [ спецметка переменная-параметр ]... ¬ [ given метка:переменная-параметр [, метка:переменная-параметр ]...] [ оператор ]... end событие
Имена меток можно выбирать какие угодно, но есть некоторое количество наиболее распространенных, использование которых упрощено. Среди этих спецметок: above, from, around, at, below, beside, between, by, for, from, instead of, into, over, thru, under и некоторые другие.
При вызове такого обработчика порядок фактических параметров (за исключением «непосредственного» — непомеченного, который должен всегда идти первым) роли не играет. Для логических параметров можно при вызове вместо указания значений «false» или «true» ставить перед соответствующей меткой соответственно «without» или «with» (в этом случае, к тому же, можно обойтись без «given»).
Например, несколько модифицируем подпрограмму поиска минимума, чтоб она могла при необходимости округлить полученный результат:
on minimum of numList given rounding: roundFlag local min set min to item 1 of numList repeat with numRef in numList set num to contents of numRef if num < min then set min to num end repeat if roundFlag then set min to (min + 0.5) div 1 return min end minimum
Вызвать ее можно будет либо так:
minimum of {2.5, 5, 3.2, 43, 5.1, 1.5, 3, 7} given rounding:true
либо вот так:
minimum of {2.5, 5, 3.2, 43, 5.1, 1.5, 3, 7} with rounding
В скриптах, сохраненных как приложения (Application либо, в новом ScriptEditor, Classic Applet/MacOS X Applet) используются обработчики для нескольких системных событий.
Во-первых, любая программа при запуске получает сообщение «run». Обработчик для него можно специально не оформлять, так как по умолчанию им считается все, что не входит ни в какие другие обработчики.
С другим событием Вы уже сталкивались в первой части статьи. Это — «open». Его обработчик — основной элемент дроплетов — оформляется вот так:
on open список файлов [оператор]... end open
Еще два события могут обрабатываться только «остающимися открытыми» (stay open) апплетами. Наиболее важное из них — «idle» — периодически передается программе, если не происходит никаких других внешних событий. Это очень удобно, если Вы хотите заставить компьютер выполнять в фоновом режиме какую-либо операцию через определенные промежутки времени. Обработчик не имеет никаких параметров:
on idle [оператор]... end idle
Есть здесь одна тонкость. Первоначально пауза между событиями idle устанавливается в 30 секунд, а затем (Внимание!) — приравнивается значению, возвращенному обработчиком (если это — число). Поэтому, чтобы избежать непредсказуемого поведения скрипта, рекомендую последним оператором обработчика записывать return — естественно, с параметром-числом, равным необходимому интервалу в секундах.
Попробуйте подложить коллеге в StartupItems вот такой простенький скрипт (не забыв при сохранении сделать его «stay open» и отключить стартовый диалог). Если приятель не слишком хорошо знаком с AppleScript, несколько приятных минут Вам обеспечено…
tell application "Finder" set frontmost to true end tell on idle beep return 2 end idle on quit display dialog "Hello! \rI'm virus!" end quit
В этом примере использован обработчик еще одного события — «quit». А написан он таким образом, что на все попытки пользователя корректно завершить работу скрипта (через команду меню или ее эквивалент — Command-Q) будет просто выводится диалог, после чего «биканье» продолжится. На всякий случай, подскажу: можно завершить скрипт, минуя обработчик quit, если нажать Command-Shift-Q.
Ну, а на самом деле этот обработчик предназначен, конечно, не для таких «мелких пакостей». Например, иногда нужно подстраховаться от случайного завершения программы:
on quit display dialog "Завершить?" ¬ buttons {"Нет", "Да"} default button "Да" if the button returned of the result is "Да" then continue quit end if end quit
Обратите внимание: чтоб апплет мог корректно завершиться, в обработчике обязательно должен выполниться оператор «continue quit».
Последний вопрос, которого нам совершенно необходимо коснуться — три вида переменных в AppleScript: свойства, глобальные и локальные переменные.
Свойства объявляются и сразу же получают значения:
property theCount: 0
Глобальные и локальные переменные при объявлении никаких значений не получают:
global fileName local first, last
Значения и свойств, и переменных могут быть изменены с помощью set или copy. Если в этих методах встречается переменная, ранее не объявленная, она будет создана локальной.
На первых порах Вам достаточно знать, что свойства и глобальные переменные могут быть использованы из любого места скрипта (причем, что интересно, их значения сохраняются и между запусками), а локальные — только в том обработчике, где они были объявлены.
Ну, что ж, закончим на этом наш «разговорник». Как справедливо замечено, нельзя объять необъятное. Не скрою, на эти страницы не попало еще очень много интересных и полезных элементов AppleScript. Возможно, мы еще вернемся к описанию языка. А пока, думаю, теорией я Вас уже загрузил изрядно. В следующей раз мы, обещаю, вновь возьмемся за наш верный ScriptEditor и…