ACL - advanced command language - входной язык программы-интерпретатора ACL
URL программы: http://kohnvict.ucoz.ru/vkacl/ACLnews.htm
автор - Виктор Кон (Курчатовский институт), URL: http://kohnvict.narod.ru
ПК - компьютер с большим экраном и разрешением 800*600 и более
программа ACLP.jar для ПК написана на языке Java SE 6u0, URL: http://java.sun.com
1. ОБЩАЯ ИНФОРМАЦИЯ
Язык программирования ACL был придуман в 1992 году для компьютеров PC AT-286, работающих под ДОС. Тогда назрела необходимость как-то управлять большим количеством уже написанных подпрограммм на языке Fortran. Использовать библиотеки на стадии компиляции оказалось малоэффективным и хотелось иметь как раз чистый интерпретатор, чтобы как двое из мешка: написал и сразу исполнялось. Так получилось, что ACL по своей структуре оказался языком макросов, что как раз соответствовало поставленной цели. С появлением Windows старый интерпретатор - ДОС программа - потерял свою актуальность. Через десять лет я взялся реанимировать ACL сначала с целью использовать его на КПК, так как хороших программ для расчетов на карманном компьютере к моменту написания моей программы я не нашел. Да и сейчас их нет. Для написания интерпретатора я использовал язык Java в модификации для виртуальных машин проекта Ewe. Эти ВМ давались бесплатно с правом свободного распространения, что естественно привлекает. Фактически надо было переписать старый код с фортрана на Java. Но получилось так хорошо, что я решил сделать отдельную версию для реализации на ПК и с использованием стандартного языка Java SE 4u2. Это была последняя версия к моменту начала работы. В последующем обе версии стали слегка различаться по причине разных возможностей у КПК и ПК. Но главная структура языка все же общая. Я буду писать о языке для ПК, язык для КПК фактически умер вместе с ОС Винловс Мобайл, где он работал.
Основными структурными единицами языка ACL являются команды, параметры и аргументы. Типичная ACL программа -- это набор команд для исполнения как в любых командных файлах ДОС или UNIX, хотя в моем случае аргументы некоторых команд имеют очень сложную структуру и сами могут иметь команды. В тексте программы привязка команд к строкам отсутствует, а комментарии являются нормальным текстом. Команды могут находится между таким нормальным текстом в любом месте. Другими словами, комментарии пишутся естественным образом, а программа - как оазисы в пустыне. Интерпретатор пропускает текст в файле до тех пор, пока не находит символ [#]. Я буду нужные символы помещать в квадратные скобки, чтобы отделить от текста. Этот символ указывает на начало команды. За ним без пробелов должно быть набито имя команды. Можно писать как угодно длинное имя, но интерпретатор будет принимать во внимание только первые буквы. Для некоторых команд достаточно одной буквы, для каких-то команд 2-х букв, но для всех команд не более 3-х букв. Число команд в принципе не ограничено, но пока их 37.
После имени команды через пробел (за редкими исключениями) проверяется наличие специального поля определения параметров, я его называю FPD (field of parameter definition). Это некоторый набор операций присваивания для определения значений параметров, ограниченный квадратными скобками. Существует 4 текстовых параметра, значениями которых являются строки символов, остальные параметры -- это целые переменные, значениями которых являются целые числа. Все параметры имеют имена. С именами параметров такая же история -- важны только первые буквы, хотя имя может быть и длинным, если кому то так удобнее. Один раз определенный параметр сохраняет свое значение пока не будет переопределен. Определять и переопределять любые параметры можно в FPD любой команды. При запуске программы все параметры определены. Поэтому, если вы не определили какой-либо параметр, то команда сначала будет работать правильно, но потом, после того как этот параметр переопределен, могут быть ошибки. Поэтому лучше все же определять все параметры, используемые данной командой, в ее FPD, или по крайней мере следить за их текущим значением. Наконец, после FPD нужно писать аргументы команды, если согласно ее определению это необходимо. В отличие от параметров, которые имеют имена и которые можно определять сколько угодно раз, аргументы считываются только один раз и являются значениями. Поэтому аргументы надо определять всегда. Аргументы задаются без скобок.
Но не все так просто. Среди текстовых параметров есть один специальный параметр, называемый [operation] или короче [op]. Этот параметр указывает какую операцию данная команда будет выполнять из числа возможных. Число операций для каждой команды тоже в принципе не ограничено и зависит лишь от версии языка и работоспособности автора интерпретатора. В зависимости от типа операции команда может использовать различные параметры и аргументы. То есть одна и та же команда может иметь различную структуру. Как известно, в объектно-ориентированных языках есть все же статические классы, работающие со статической памятью. Язык ACL частично похож на язык со статическими классами и статической памятью. Команды аналогичны статическим классам, а операции похожи на статические методы классов. По законам жанра должна быть и статическая память.
Действительно, в языке ACL память общая для всех команд во всех частях ACL-программы, то есть статическая. Современные языки умеют оперировать с временной памятью, которая динамически создается и динамически убирается. С другой стороны, в языке PostScript вообще нет памяти в обычном понимании -- только стэк. В ACL реализован промежуточный вариант, который наиболее подходит для основной цели языка - организации вычислений с использованием готовых блоков. Использовать интерпретатор на микроуровне для суперсложных расчетов с большим числом повторений кода неразумно из-за резкого снижения скорости выполнения операций по причине необходимости интерпретировать каждую команду снова и снова. Но у интерпретатора есть свои преимущества и вот их как раз полезно использовать. И статическая память в этом случае тоже полезна, так как не надо думать о передаче данных из одной ACL-процедуры в другую.
И снова упрощение. Я не люблю напрягать голову придумывая и запоминая имена переменных. Поэтому я сначала сделал только однобуквенные имена переменных от [a] до [z], затем от [A] до [Z] и дополнительно специальные переменные [$] [%] [&]. Всего 55 имен переменных. При этом большие и маленькие буквы означают разные переменные. Но потом я расширил число переменных, что все же необходимо для организации достаточно сложных расчетов. Как показала практика, язык ACL оказался вполне способен достаточно быстро выполнять сложные аналитические анализы. Поэтому я ввел дополнительно переменные, имена которых имеют два символа - букву и цифру, то есть a0,b0,...,z0,a1,b1,...,z9,A0,...,Z9. Всего 520 дополнительных имен переменных. Так когда-то было в бейсике для первых примитивных персональных компьютеров, на которых мне удалось поработать. Ну а через много лет я ввел еще и третий тип переменных, состояших из двух букв. Их уже 55*55=3025 имен. Кроме того, есть один целый массив
Несмотря на некоторые ограничения, такая структура языка позволяет создавать программы любой степени сложности, которые могут работать как интерактивные и многооконные приложения, причем в любой операционной системе. Они могут использовать все ресурсы самых современных компьютеров, так как интерпретатор написан на Java, и использует почти все, что Java предлагает.
2. ПЕРВЫЕ КОМАНДЫ
Как это выглядит практически. Прежде всего есть команда окончания
#stop
которая заканчивает чтение файла и выполнение команд, даже если в нем еще остались команды ACL. Это удобно, так как позволяет хранить в одном файле куски программ для примеров и подсказок. Если такой команды нет, то файл читается до конца и программа заканчивается естественным образом. Есть еще команда
#exit
позволяющая завершить работу интерпретатора. Она необходима только при запуске интерпретатора с полностью готовой ACL-программой, когда среда отладки ACL-программ у интерпретатора не используется совсем. Это позволяет представить ACL-программу как независимую программу, при использовании которой знание языка ACL не требуется. Далее, в языке есть описания процедуры
#pro NAME <здесь помещаются все другие команды> @
Символ [@] служит окончанием процедуры (жирная точка). Имя процедуры NAME является первым аргументом такой команды. Оно должно содержать 4 знака в любой комбинации. Любой кусок программы можно предварительно поместить в тело процедуры (он при этом не выполняется, а запоминается). Тело процедуры и символ окончания [@] можно рассматривать как второй аргумент. Этот код будет выполнен позже. Порядок записи на строке не имеет значения. Как удобно, так и записываете. Процедуры выполняются с помощью специальной команды, которая должна естественно быть поставлена после определения процедуры
#e [c=1;] NAM1 NAM2 NAM3 !
Эта команда означает выполнить (execute) процедуры одну за другой из указанного списка. Если у команды #pro есть только аргументы: [NAME] и сама процедура, то у команды #e кроме аргументов есть уже и параметр с именем [c] (code), значение которому можно присвоить прямо тут же, в FPD, а можно и раньше. Все процедуры выполняются только в том случае, если значение параметра [cod] совпадает с текущим значением переменной [&]. Эта переменная должна быть определена заранее. Но если написать
#e [c=&;] NAME !
то процедура всегда выполнится при любом значении [&]. Для сокращения, то же самое можно написать как
#e _NAME !
Символ подчеркивания перед первым именем в списке процедур означает безусловное выполнение. Важно помнить, что [c] - это целое число, а [&] - это вещественная переменная, поэтому для сравнения берется ее ближайшее целое. Список имен заканчивается либо на следующей команде, то есть символе [#], либо символе окончания процедуры [@], либо на специальном символе [!] если после списка идет простой текст.
Эта команда усилена дополнительно двумя специальными режимами, необходимыми для написания особенно сложных программ. Специальные имена процедур [file] и [text] служат для выполнения команд программы из файла или из текстового массива t(). Пример:
#e [file=0/calcul.acl;] _file !
Эта команда указывает, что нужно выполнить процедуру, то есть набор команд, записанных в файл "calcul.acl", помещенный в папку "0" относительно папки интерпретатора. Интерпретатор первоначально специально был сделан так, что он не работал с внешними папками, но работал с папками внутри "нулевой" папки, то есть папки, в которой находится сам интерпретатор. (Папка) -- это место, где хранятся файлы, другое ее название (Директория) использовалось в ДОСе, я перешел на оконный язык. Здесь уже используется второй текстовый параметр [file] для указания пути к файлу. В этой команде путь к файлу указывается полный, то есть относительно "нулевой" папки. Еще более сложная команда
#e [b=a; le=b;] _text #%
позволяет выполнить команды, динамически записанные в часть текстового массива t(), начиная c элемента t(a) и длиной b символов. Понятие строки текста в ACL не существует. Любая строка, как известно, состоит из набора символов и имеет имя и длину. Значения числовых параметров [b] (begin) и [le] (length) можно рассматривать как имя и длину строки. Просто в ACL имя строки - тоже число, которое указывает размещение символов в текстовом массиве. Этого вполне достаточно. Программа, которую выполняет данная командам, исходно не существует, поэтому ее надо предварительно записать. Естественно и параметр [c] тоже можно использовать в обоих случаях. Записанный вариант безусловного исполнения просто короче. Наконец, нужно отметить одно важное свойство этой команды, о котором я уже немного написал выше. Она читает имена процедур для выполнения неограниченно, пока не встретит символ [#] или символ [@], означающие начало новой команды или окончание процедуры. Если после команды необходимо вставить текст комментария или данная команда является последней в файле, то после нее можно поставить любую другую команду с фиксированной длиной. Но для краткости записи введен символ [!], который тоже останавливает чтение списка. Самой короткой командой является #% поэтому я раньше использовал эту команду. Ее реальный смысл -- засылка текущего номера символа текста программы в переменную [%] для последующего использования в разных целях. Но в данном случае она просто играет роль ограничителя предыдущей команды. Если этого не сделать, то интерпретатор выдаст ошибку несуществующей процедуры.
Как это очевидно из сказанного, сначала надо определить все процедуры, а потом писать текст основной программы. Иногда это бывает неудобно, так как приходится начинать изучение программы с конца файла. Но можно использовать простой прием. Основной текст программы тоже поместить в процедуру
#pro main ... @
Теперь уже можно писать любые процедуры в любом порядке, а в конце программы написать стандартную строку
#e _main #stop или #e _main !
Второй вариант необходим, если программа записана в файл и является частью более сложной программы, поэтому остановка нежелательна. Последнюю строку смотреть не обязательно. В таком виде вся программа состоит из процедур.
3. ПЕЧАТЬ И ПРОСМОТР ТЕКСТОВ
Текстовый массив t() -- это массив символов, но не старых ASCII символов, а юникодов (двухбайтовых символов). В его элементы можно вводить (писать) символы любого языка (юникоды), числа в текстовом формате, просто текст и многое другое. Для записи в текстовый массив существует команда
#print FT (короче #pr FT)
Она имеет один агрумент, называемый formatted text (FT). FT -- это в общем случае сложный форматированный текст, состоящий как из простого текста, так и из текстовых команд (форматов). Форматы, как в языке Fortran, на Java отсутствуют, это мелкое дело и создатели Java считают, что форматы должны делать сами программисты. Но не всем интересно делать форматы, да это и неудобно в языках быстрого программирования, каким является ACL. Поэтому я сделал много разных форматов, на все случаи, но если кто-то скажет, что ему нехватает, могу без труда сделать и другие форматы, пока нет идей. Пока есть все, что лично мне нужно для тех программ, которые я уже написал.
Прежде всего важно знать, что FT всегда заканчивается признаком конца, то есть командой окончания. Все команды форматов начинаются знаком [\] после которого стоит одна буква (имя команды) и вслед за ней могут быть аргументы. Команда окончания имеет вид \E, например команда
#pr ab cde \E
напечатает в текстовый массив t() текст "ab cde ". Как вы уже догадались, текст начинается с любого символа, отличного от пробела. То есть в таком режиме невозможно записать пробелы в начале текста. Как это делать будет сказано позже. Все вызовы команды #pr печатают в t() последовательно запись за записью, начиная с t(1). Но такой естественный порядок можно изменить. Реально номер элемента массива t(), начиная с которого будет сделана очередная запись, определяется элементом служебного массива s(3) без имени. Значение этого элемента, в отличие от элементов с именем, автоматически меняется при каждой записи. Но это значение можно изменить и руками, приравнивая s(3) любому значению перед выполнением команды #pr. Кроме того, для удобства использования записей после каждого выполнения команды #pr элемент служебного массива s(4) содержит начало произведенной записи, то есть индекс первого элемента текстового массива, а s(5) - число записанных символов. Следующая команда -- это команда записи в текстовый массив элементов самого текстового массива. Она имеет два аргумента
#pr ab \T<b> <l> cd\E
Здесь аргументы <b> (beginning) и <l> (length) содержат информацию о первом элементе текстового массива и о длине текстовой строки для записи. Это могут быть целые числа, переменные или элементы числовых массивов. Опять отмечу, что после команды \T (а точнее после всех команд) пробелы игнорируются и последующий простой текст начинается с любого символа, отличного от пробела. Например,
#pr ab \T121 10 cd\E
запишет следующий текст [ab t(121)...t(130)cd]. Вы уже догадались, что такая текстовая команда позволяет выполнять любые манипуляции со строками текста, хотя самого понятия текстовой строки в языке ACL нет. Это особенно полезно в циклах.
Следующая серия команд - это команды записи в текстовый массив значений числовых переменных. Самая простая из них \G может иметь сколько угодно аргументов, разделенных пробелами. Список аргументов заканчивается знаком [;]. Например, правильная команда
#pr \G a b c d;\E
Здесь a, b, c, d - это числовые переменные. Пусть например, a=345; b=0.001; c=2.E-4; d=0; После выполнения указанной выше команды будет записан следующий текст
3.4500+02 1.0000-03 2.0000-04 0
То есть формат \G - это общий (general) или стандартный формат, который все числа записывает в строку из 11 символов с пятью значащими цифрами, с десятичным разделителем в виде точки и с показателем степени без знака E. Я заметил, что знак E необходим при задании чисел в математических выражениях, но при распечатке чисел через пробел он является лишним и его можно опустить. Такой формат удобен, если вам нужно получить значения чисел, а фасон вам не важен. Однако если фасон важен, есть другая команда
\N<t> <f> a b c; ,
имеющая два дополнительных форматных аргумента. Здесь <t> -- число, указывающее полное число знаков при печати чисел, а <f> - число, указывающее размер дробной части при записи чисел. Показатель степени при этом отсутствует. Все остальное как в команде \G. Очевидно, что <t> должно быть больше чем <f> не менее чем, на две позиции + длина целой части числа. Однако есть специальные случаи. Если <t> равно 0, то длина записи числа минимальна и зависит лишь от величины целой части. Если <t> равно -1, то данная команда эквивалентна \G. Так исторически сложилось, что команда \G была введена позднее как замена этому режиму. Дополнительно есть еще формат
\B<t> <f> a b c; ,
где аргументы имеют то же самое значение, как в и команде \N, но число записывается с одним целым разрядом и с показателем степени десяти после буквы E. Кроме того, в этом формате десятичным разделителем служит точка вместо запятой. Этот формат необходим для совместимости записи данных с другими программами. В некоторых программах удобно вставлять в текст целые числа с фиксированным числом разрядов. Для этого есть команда
\I<n> a b c; ,
где n есть полное число разрядов, отводимое для числа. Если оно положительное, то лишние разряды слева заполняются пробелами, а если отрицательное, то нулями. В последнем случае число разрядов показывает модуль аргумента n. В отдельных специальных случаях используется команда
\S a; ,
которая записывает число с минимальным числом разрядов и, кроме того, округляет последнюю значащую цифру, чтобы ликвидировать ошибки расчетов. Например, число 2.49999234 будет заменено на 2.5 так же, как и число 2.50000123. Формально эта команда тоже применяется к списку переменных, но реально разумно ее использовать только для одного значения, так как два значения напишутся слитно (без пробелов). Эта команда полезна для разметки осей графиков и вообще на графических рисунках, а также в окнах ввода. Так было в самом начале, потом я все-таки окружил числа пробелами.
Следующая команда -- это печать символов по их юникоду. Юникоды, как известо, описывают знаки любого языка. Будут ли они напечатаны, зависит от возможностей используемого фонта. Но они точно будут введены и ими можно манипулировать. Первые 128 юникодов совпадают с ASCII кодами. Команда выглядит так
\U n n n*m n n; .
Замечу, что пробел после буквы U необязателен. Конструкция n*m означает, что уникод с номером n будет напечатан m раз. Список заканчивается [;]. Вот теперь ясно, что 5 пробелов в самом начале текста можно ввести как \U32*5; . Есть также система более простых команд для ввода в текст служебных сиволов, а именно
\b (backspace)
\t (tab)
\n (line feed)
\r (carriage return)
\' (символ ')
\" (символ ")
\\ (символ \)
\@ (символ @)
\# (символ #)
\[ (символ [)
\] (символ ])
\- (ничего).
Эти команды за исключением последней вводят те символы, которые указаны вторыми, так как непосредственная запись этих символов будет приводить к неправильной интерпретации и ошибкам. Последняя команда \- совсем ничего не делает. Но она все же необходима, так как не только пробелы, но и знаки конца строки игнорируются после каждой команды. Очень длинный текст в аргументе команды #print набивать в одной строке неудобно и его разумно перенести. Но тогда запишется признак переноса строки. Чтобы этого не случилось, перед переносом нужно набить \- и тогда знаки переноса строки в текст не запишутся, а сама команда ничего не добавит. А если на самом деле нужно, чтобы признак конца строки записался, то пишите смело в несколько строк -- так и будет напечатано.
Наконец, есть еще команда
\D ,
которая вставляет в текст текущие дату и время по данным на компьютере, есть команда
\P<n> ,
которая вставляет значение текстового параметра. Текстовых параметров 4, но изначально было 6, поэтому они имеют номера со скачком. Можно сказать, что два номера зарезервированы. Так [fir] (first) имеет номер 1, [op] (operation) имеет номер 4, [file] и [form] (format, formed) имеют номера 5 и 6 соответственно. И есть еще скобки комментария
![...]! .
Как сами эти скобки, так и текст внутри них игнорируется и не записывается. Комментарий можно ставить после любой команды перед обычным текстом. Но реально это уже излишество и необходимо только в очень редких случаях. Я сам такие комментарии не ставлю. Впрочем была бы возможность, а как ее использовать каждый придумает сам.
Как я уже отмечал, такая совокупность форматов позволяет охватить все необходимые ситуации. Со временем были сделаны и другие форматы, но это смотрите уже в более свежих документах. А данный документ был записан в самом начале. Текстовый массив можно просмотреть после выполнения программы из меню оболочки [Execute//Show textarr], так как после выполнения программы записанная часть текстового массива спасается в файл с названием "textarr.txt". Вот этот файл и показывается в редакторе текста. Размер записанной части текстового массива определяется параметром s(6). Если в текстовый массив записано очень много текста и нежелательно его записывать в файл "textarr.txt", так как это занимает время, то можно просто обнулить этот параметр перед окончанием программы.
Есть другая возможность показа форматированного текста без записи в текстовый массив. Для этого служит команда #message или короче #m. Эта команда показывает форматированный текст различным образом в зависимости от значения параметра [op]. Если задана команда
#m [op=txt;] FT,
то текст будет показан в окне текстового редактора, который сопровождает работу программы и называется "Терминалом". Все режимы этой команды показывают текст в процессе работы программы как раз при ее выполнении. Если
#m [op=rtt; n=1;] FT,
то в тексте, ранее набитом в терминале будет удалено столько символов, сколько указывает значение параметра [n] (number) (в показанном варианте 1). В этом случае аргумент не используется, но он все же должен присутствовать хотя бы в пустом виде \E для соблюдения синтаксиса. Этот режим позволяет контролировать степень выполнения некоторой процедуры в цикле. Можно сначала напечатать некую строку текста, а затем по мере выполнения цикла вычеркивать символы, то есть делать обратный отсчет. Если
#m [op=win;] FT,
то текст будет показан в отдельном окне и работа программы будет остановлена до тех пор пока пользователь не нажмет клавишу [OK]. Если
#m [op=oce;] FT,
то текст будет показан в специальном окне, содержащем три клавиши [OK] [Cancel] {Edit] и номер нажатой клавиши будет равен значению специальной переменной [&]. Если
#m [op=yno;] FT,
то текст будет показан в специальном окне, содержащем две клавиши[Yes] [No] и номер нажатой клавиши будет равен значению специальной переменной [&]. Наконец,
#m [op=uni;] FT FT,
(эта команда имеет два аргумента FT) показывает сообщение в окне, атрибуты которого определяются вторым аргументом FT. Этот аргумент должен содержать несколько текстов, разделенных знаком вертикальной черты [|]. При этом первый текст - это заголовок самого окна, а остальные - это заголовки кнопок, число кнопок будет зависеть от числа записанных текстов. Номер нажатой кнопки в диапазоне от 1 и до номера последней кнопки возвращается в [&].
Команда #m (достаточно одной буквы) является одной из серии команд, реализующих интерфейс с пользователем. Она позволяет сообщать различную информацию и задавать вопросы. Ее можно использовать для организации справочников, картотек и т. д.
4. ВЫЧИСЛЕНИЯ
Теперь о математических расчетах и функциях. Все расчеты делаются в той части текста программы, которая является аргументом специальной команды #calc или #с. Есть еще вариант этой команды, когда ее можно писать вообще без имени, так как учитывая частое использование этой команды для нее сделано исключение. Она не имеет параметров, а ее аргументом является совокупность операторов присваивания. Например,
# a=1; b=sin(a); c=b<2; i(5000)=&;
Итак два знака [# ] (пробел после решетки обязателен) открывает поле операторов присваивания с конструкцией <name>=<value>; причем пробелы между этими элементами могут быть в любом количестве, в том числе и отсутствовать. Можно все писать совсем без пробелов. Это поле заканчивается либо знаком [#], то есть началом новой команды, либо знаком [@], то есть концом процедуры. Можно ставить комментарии специальной конструкции, но проще оборвать поле символом [!]. Этот символ мы уже видели при описания выполнения процедур. Тут он используется с тем же смыслом. Для выполнения вычислений есть все стандартные операции
+ - * / ^ ( )
записанные в обратном порядке очередности выполнения. Знак ^ означает возведение в степень. И есть стандартные функции. Последние имеют имена, состоящие из трех знаков. Список стандартных функций дополнен некоторыми специальными функциями. Он перечислен ниже:
abs() [абсолютное значение]
sqr() [квадратный корень]
log() [натуральный логарифм]
exp() [экспонента]
sin() [синус]
ars() [арксинус]
cos() [косинус]
arc() [арккосинус]
tan() [тангенс]
art() [арктангенс]
rnd() [случайное значение]
int() [целая часть]
sfi() [синус интеграл Френеля int(0,x)dt*sin((pi/2)*t^2)]
cfi() [косинус интеграл Френеля int(0,x)dt*cos((pi/2)*t^2)]
bj0() [ функция Бесселя J[0](x)) ]
bj1() [ функция Бесселя J[1](x)) ]
Аргумент у функций записывается в круглые скобки так же, как и индексы элементов массивов. У функции rnd() аргумент не используется, но для соблюдения грамматики надо писать rnd(0). Числа можно писать в любом формате как с точкой, так и с запятой и в экспоненциальной форме как с [E] так и с [e]. Вот примеры правильной записи чисел:
1,23 1.23 12,3E-1 0.123E1 123e-2
Но в языке ACL есть особенность, отличающая его от других языков, а именно, добавлены две новые операции
К команде расчетов естественным образом примыкают еще две простые, но полезные команды. Первая из них
#d N r(11) a b c*5 g ...
Имя этой команды (data) но достаточно одной буквы. Она позволяет быстро определить значения элементов массивов. Первый аргумент (N) определяет сколько элементов будет определено, второй просто указывает первый элемент массива для определения. На самом деле эта запись задает как тип массива, так и первый индекс. Важно иметь в виду, что переменные тоже образуют массив, специальным образом упорядоченный, так что их тоже можно определять таким способом. А после идут значения, которые надо присвоить элементам массива. Это могут быть числа, переменные и элементы массивов. Если после значения стоит *5, то это означает, что данное значение нужно взять 5 раз. Итак аргументы команды образуют следующую структуру: <число><начало массива><данные>. Но таких структур может быть сколько угодно, одна структура следует за другой. Окончание опять либо на знаке [#], либо на знаке [@], либо на знаке [!]. Все остальное трактуется как ошибка. Число N ограничено и не может быть больше 500. Есть и обратная команда, ее имя v и она пересылает значения из массива в пременные.
Вторая команда
#pas N r(11) i(25)
позволяет быстро присвоить значения из элементов одного массива элементам другого массива, как говорят, переслать значения из одного массива в другой. Если массивы имеют разный тип, то параллельно происходит преобразование типа. Первым указывается массив из которого пересылаются значения, вторым - куда пересылаются. Например, в укзанном примере вещественные числа будут округлены до ближайшего целого. В этой команде структура не повторяется. Надо снова писать имя команды. Число N не ограничено.
5. УСЛОВНОЕ ВЫПОЛНЕНИЕ
В результате введения операций [<] и [>] оказалось возможным упразднить все так называемые условные операторы, оставив только блок из двух команд. А именно
#case N . . . #end
Здесь N есть аргумент команды #case -- это число, переменная или элемент массива, а многоточием обозначены все другие команды, которые выполняются лишь в том случае, если [N=&]. Команда #end ограничивает код одного варианта и проводит дополнительный анализ, а [&] - опять специальная переменная, которая играет важную роль в интерактивных окнах. Самой простой логической операцией, которую можно реализовать с помощью этой команды является ветвление. Допустим у нас есть несколько вариантов кода, из которых надо выполнить какой то один. Это делается набором команд
# &=2; #case 1 . . . #end | #case 2 . . . #end | #case 3 . . . #end | и так далее.
В написанном коде будет выполнен второй вариант. Достаточно поменять значение переменной [&], не трогая остальной код, и будет выполнен новый вариант. Но пара команда
# i=0; &=i; #case 0 . . . . # i=i+1; &=(5-i)<0; #end
Здесь многоточие выполнится для i=0,1,2,3,4,5 и затем цикл закончится и программа пройдет дальше. В такой конструкции #case работает как простой цикл. Но для удобства для этих целей есть и простая команда
#rep N . . . #end
которая просто N раз повторяет многоточие. Команда #case реально позволяет выполнить или не выполнить что-то в зависимости от любого логического условия. Нужно только правильно определить переменную [&] используя операции [<] и [>] . То есть она одна заменяет и оператор if(){} и оператор while(){} и оператор switch(){} языка Java, а также аналогичные операторы других языков. И в то же время никаких ограничений на возможности программирования не накладывает.
6. ПЕРВЫЕ ИТОГИ И ОБЩАЯ ИНФОРМАЦИЯ
Подведем первые итоги. У нас есть переменные, массивы и тексты. Мы умеем задавать значения переменным, проводить вычисления в циклах и в зависимости от различных условий, умеем все это записывать в виде текстов и показывать на экране в виде сообщений. Не так уж и мало для выполнения расчетов, оформления тестов, даже написания информационных картотек. Но арсенал не полон. Даже самые первые и примитивные языки программирования типа бейсика и фортрана умели записывать и считывать данные из файлов. Правда в нашем случае, в отличие от компилируемых языков, сам текст программы может содержать внутри себя все необходимое, что обычно записывают в файлы. Но дело в том, что нужно уметь оперировать файлами, записанными в специальном формате или другими программами. А более продвинутые современные языки умеют создавать также и средства графического общения с пользователем. Не беспокойтесь. Все это есть в ACL и даже немного больше, чем в других базовых языках. Но этими средствами надо научиться пользоваться. И это не всегда просто, так как требует знаний о работе компьютера, об операционной системе, о структуре и способах записи файлов. Впереди еще много работы и делать ее надо постепенно и планомерно. Но прежде, чем продолжать, перечислим команды, которые мы уже знаем.
stop | exit | pro | e | pr | m | c | d | v | pas | case | end | rep
и обсудим некоторые детали организации памяти RAM (random access memory) и HD (hard disk). Первая -- это оперативная память компьютера, она работает только при включенном компьютере и пропадает при его выключении, вторая -- это память на жестких носителях, которую можно переносить из одного компьютера в другой и хранить долгое время. Язык ACL -- это всего лишь набор данных для программы, написанной на языке программирования Java. Поэтому он оперирует с теми же типами данных, что и Java. В Java определены следующие типы данных
byte - целые числа длиной 1 байт со знаком и 27=128 по модулю
short - целые числа длиной 2 байта со знаком и 215=32678 по модулю
int - целые числа длиной 4 байта со знаком и 231=2147483648 по модулю
long - целые числа длиной 8 байт со знаком и 263 по модулю
float - вещественные числа длиной 4 байт со знаком, точность 7 десятичных знаков
double - вещественные числа длиной 8 байт со знаком, точность 15 десятичных знаков
char - двухбайтовые символы - юникоды.
Реально для положительных чисел модуль на единицу меньше, так как есть еще ноль. В языке ACL переменные и элементы реального массива имеют тип double, а элементы целого массива имеют тип int. Элементы текстового массива имеют тип char. Остальные типы данных в расчетах не используются, но при записи в файлы могут использоваться.
Что касается файловой структуры на жестких носителях, то она делится на диски, обозначаемые обычно одной буквой (a-дискета, b-второй дисковод, который сейчас бывает очень редко, с-системная часть винчестера, d-программная часть винчестера, но не всегда, остальные буквы обозначают компакт-диски, флеш-память и так далее). Каждый диск имеет набор вложенных папок, внутри которых параллельно с папками могут находиться файлы. Имя файла поэтому может быть как коротким (собственно имя) так и длинным (то есть путь к файлу). В коротком имени файла могут присутствовать одна или несколько точек. Часть имени после последней точки обычно называют расширением имени и используют для определения типа файла. Например, exe - для исполняемого файла, acl - для файла с кодом программы на языке ACL, java - для файла с кодом программы на языке Java, jar - для файла с архивом кода программы на Java и так далее. Например, aclp.jar -- это архив кода программы на Java и одновременно исполняемая программа -- интерпретатор ACL.
Полный путь к файлу на Java (и ACL) записывается так
C:/перваяпапка/втораяпапка/третьяпапка/файл
но можно писать укороченный путь относительно файла какой-нибудь программы. Пусть, например, файл aclp.jar имеет такой путь
C:/_vk/ACLp/aclp.jar
Тогда укороченный путь относительно данной программы для файла, который находится в любых папках внутри папки программы будет такой, в котором
C:/_vk/ACLp/
не пишут. В ACL раньше использовались только укороченные пути файлов, то есть все используемые файлы ОБЯЗАТЕЛЬНО должны были находиться в папке интерпретатора или в более внутренних папках. Более того, в ACL есть понятие "рабочей папки", которую можно определить заранее и тогда имена файлов многих команд отсчитываются от этой рабочей папки. Последнюю можно переопределять по ходу выполнения программы.
А рабочая папка с именем null использовалась как раз для папки интерпретатора. Потом все же было сделано исключение, и если рабочая папка null а в пути файла второй символ (:), то используется полный путь файла на компьютере. Ну вот этой информации вполне достаточно, чтобы перейти к командам работы с файлами.
7. ОПЕРАЦИИ С ФАЙЛАМИ
Описанная выше команда #d определяет элементы массивов по значениям. Но часто бывает полезно записать значения в файл и потом считать эти значения в другой программе или в той же самой, но в другой день. Такие операции выполняет команда
#io [op=; fir=; n=; file=; form=; mo=; le=;]
Она использует 4 текстовых параметра и один числовой параметр. Параметр [op] задает тип операции, который кодируется двумя бувами. Первая буква может быть: [r] (read - прочитать из файла) или [w] (write -записать в файл). Вторая буква может быть: [f] (format - запись числовых значений текстом по формату) или [b] (byte - запись данных типа byte) или [d] (data - запись данных типа float, int или double) или [t] (text - запись текстовых данных). Итак возможны операции
rf wf rb wb rd wd rt wt
Далее, параметр [fir] (first) равен записи первого элемента массива, как это было в командах #d и #pas, то есть указывается и тип и первый индекс, например, r(35). Параметр [n] (number) указывает сколько элементов надо записать или считать. Параметр [file] указывает путь к файлу относительно "рабочей папки". Если рабочая папка задана правильно, то достаточно имени файла. Здесь есть ограничение -- значение параметра [file] не может быть длиннее 42 символов. Так что не увлекайтесь длинными именами. Если имя файла будет длиннее, его придется переименовать. Здесь полезно обсудить два различных способа задания текстовых параметров. Их можно задавать прямым текстом, например,
file=mydata.dat;
и при этом текст должен заканчиваться символом [;] как все операторы присваивания. Но можно задавать и форматированным текстом. Но при этом первым символом должен быть символ текстового формата [\] а заканчиваться такая строка должна символами \E без точки с запятой. Хотя все текстовые параметры можно так задавать, этот режим в основном используется только для задания файла и главным образом путем указания текстовой ссылки. Это очень удобно в циклах. Допустим вам в цикле необходимо прочитать 50 файлов с именами: img01.png, img02.png, . . . , img50.png.
Тогда код может быть таким.
# m=0; ... #pr img\E # a=s(4); b=s(5); #rep 50 # m=m+1; ... #io [op=rb; fir=i(1); n=256; file=\Ta b\I-2 m;.png\E] #end
Естественно, что файл, задаваемый текстом не должен начинаться с символа [\]. Но это ограничение очень слабое. Этот прием называется динамической генерацией имени файла. Более сложные имена файлов можно сначала все напечатать, а затем указывать текстовыми ссылками, генерируя индекс ссылки (имя строки) по параметру цикла.
Параметр [form] (format - формат записи чисел текстом) используется только в одной операции [wf], то есть запись по формату. В операции [rf], то есть чтение по формату, автоматически читаются все возможные форматы и специально нет смысла указывать формат.
Но формат все же используется для передачи другой информации. Он должен иметь струкутуру
form=*N;
где вместо N должно быть число или переменная с целым значением, которое указывает сколько строк в файле будет пропущено прежде чем начнется чтение чисел.
Запись чисел по формату возможна только из массивов i() и r(). Если необходимо записать числа из других массивов, то их сначала надо переслать в эти командой #pas. Параметр [form] тоже ограничен по длине 42 символами, но все возможные форматы имеют меньшую длину, так что это ограничение формальное. Наиболее простым форматом является такой
form=*N;
где вместо N должно быть число, указывающее сколько чисел надо напечатать на одной строке. Этот формат эквивалентен текстовому формату \G (см. выше о текстовых форматах). Признаком конца строки будет ASCII код 10, то есть как в операционной системе UNIX и как в Java. Есть еще второй формат, который имеет структуру
form=EN.M.L;
где вместо N должно быть число, указывающее сколько чисел надо напечатать на одной строке, вместо M должно быть число, указывающее сколько позиций (символов) должно быть отведено на одно число, вместо L должно быть число, указывающее сколько позиций отводится для дробной части числа. Точки между числами служат разделителями. По этому формату строка заканчивается двумя ASCII кодами 13,10, а числа записываются в экспоненциальной форме с E и десятичным разделителем служит точка. Необходимо, чтобы M было больше, чем L+7. Этот формат эквивалентен текстовому формату \B. Например, 6 чисел, записанных по формату [form=E3.10.3;] сгенерируют текст
1.000E+00 2.000E+00 3.000E+00
4.000E+00 5.000E+00 6.000E+00
Больше форматов нет. Но реально, можно организовать сколько угодно сложный формат, если сначала записать текст в текстовый массив t(), а затем скопировать его в файл.
Итак перейдем к операциям [rt] и [wt]. Эти операции могут работать только с текстовым массивом t(), поэтому параметр [fir] должен указывать только на t(), иначе будет ошибка. Операция [rt] просто заполняет некоторую часть массива t() из файла, начиная с элемента, указанного [fir] и ровно [n] элементов. При этом сначала считывается весь файл, как текст строка за строкой, затем содержимое конвертируется в массив юникодов и берется нужное число символов. Этот режим проверялся только на первых 127 юникодах и как он работает на остальных можно проверить экспериментальным путем. После проведения операции параметр s(4) содержит индекс первого записанного элемента в t(), а параметр s(5) - число элементов. Это общий принцип записи в текстовый массив, точно также поступает команда #pr . В любой момент параметр s(6) показывает размер записанной части массива t(). Операция [wt] наоборот, сначала конвертирует нужный кусок массива юникодов в текст и затем записывает в файл строка за строкой. При этом файл записывается целиком, а если он существовал раньше, то полностью переписывается. В этом случае признак конца строки соответсвует той операционной системе, которая используется. Например, в Windows это [13,10], хотя изначально в текстовом массиве было просто [10]. Теперь ясно, что образ будущего файла можно сформировать с помощью команды #pr и затем перенести в файл из t() целиком. При этом можно использовать всю мощь форматов команды #print.
Операции [rd] и [wd] позволяют считывать и записывать числа в компьютерном коде. Это самая сжатая форма записи и она особенно полезна для записи очень больших массивов. В этом случае дополнительно используется параметр [mo] (mode) для указания формата записи чисел. Если [mo=1;], то числа записываются и считываются в формате "float", то есть вещественные числа длиной 4 байта. Если [mo=2;], то используется формат "int" целых чисел длиной 4 байта. Наконец, если [mo=3;], то используется формат "double" для вещественных чисел длиной 8 байт. Порядок байтов как в Java, то есть старшие слева. Для данных системы Windows необходимо сначала перевернуть порядок байтов какой либо специальной процедурой. Сама ACL-программа как записывает, так и считывает и проблем не возникает. Данные считываются из файла не с самого начала, а после пропуска определенного числа байтов, задаваемого параметром [le], и не весь файл, а столько чисел, сколько указано параметром [n]. А вот записываются числа в новый файл с самого начала. Если файл существует, то он переписывается заново. Узнать размер файла при чтении можно с помощью команды #file (см. далее). Если программа считывает больше данных, чем было записано, то возникающая ошибка не блокируется и Java программа-интерпретатор выдаст сообщение на Терминал. Так что надо быть внимательным. Это недоработка, каюсь. Следует усвоить, что параметр [le] позволяет пропускать любое число данных перед чтением. Но указывается именно число байтов, а не чисел. Так что число пропускаемых чисел должно быть умножено на длину числа (4 или 8).
Операция [rb] позволяет считывать байты файла как целые числа в диапазоне от -128 до 127. В этом случае используется параметр [le] (length), который определяет число пропущенных байтов в файле, перед чтением. То есть реально считывание происходит не с первого байта, а с байта, номер котрого [le]+1. Если чтение происходит в текстовый массив, то к отрицательным значениям добавляется 1104, что переводит русские ASCII коды в формате Windows в русские юникоды. Это позволяет читать русские книги в текстовом формате Windows, что использовано в программе чтения книг. При записи операцией [wb] значения элементов массивов конвертируются в байты. При этом русские юникоды массива t() переводятся в отрицательные значения вычитанием 1104. Файл записывается сначала и заново создается. Эти операции позволяют манипулировать содержимым файла произвольным образом. Но следует помнить, что байтов в файле много, а ACL - язык не быстрый. Поэтому манипулировать разумно только с относительно короткими файлами.
Но работа с файлами не ограничивается только записью и чтением данных. Есть очень много других нужных и полезных операций. По этой причине в ACL сделана специальная команда, которая так и называется #file, а короче #f. Эта команда имеет очень много операций, причем операции объединены в одну команду лишь по той причине, что они все работают с файлами. На самом деле некоторые операции -- это целые программы со своими параметрами и аргументами и между операциями нет никакой связи. Минимальное число символов в названии операции 4, иначе трудно разделить все названия без потери смысла.
Мы начнем описание операций по принципу от простого к сложному. Первые операции -- это те, которые обычно выполняет операционная система. Там, где специально не указано, имена файлов отсчитываются от "рабочей" папки. Итак
#f [op=fold; file=any;]
Эта операция как раз переустанавливает "рабочую" папку, имя которой задается параметром [file] относительно папки интерпретатора. В имени можно использовать символ [/] для вложенных папок. После выполнения этой команды адрес "рабочей" папки меняется на всю работу интерпретатора, то есть и после завершения программы. Единственный способ переопределить "рабочую" папку -- это снова выполнить данную команду с новым значением параметра [file].
#f [op=size; file=any;]
Эта операция определяет размер файла, имя которого задается параметром [file]. Размер возвращается в s(1). Если s(1)=0, то файла не существует.
#f [op=dele; file=any;]
Эта операция уничтожает файл на диске, имя которого указано параметром [file].
#f [op=copy; n=; file=newtotal;] file1.ext\E file2.ext\E . . .
Эта команда создает новый файл с именем, которое определяет параметр [file], и с содержанием, которое является сложенными вместе содержаниями серии файлов, имена которых указывает серия аргументов в виде FT (форматированного теста). Описание FT дано выше разделе 3. Другими словами эта операция копирует несколько файлов в один. Число копируемых файлов указывает параметр [n]. При этом число аргументов может быть больше, но не может быть меньше, иначе фиксируется ошибка.
#f [op=divi; file=; form=; b=; le=; n=; xsh=;]
Эта операция, наоборот, позволяет разделить файл на части и записать одну часть в новый файл. Имя исходного файла определяется параметром [file], имя нового файла определяется параметром [form] (formed). Исходный файл трактуется как матрица байтов, из которого вынимается подматрица. Для выполнения этой работы необходимо 4 параметра. Параметр [b] (begin) определяет число байтов, которые пропускаются с самого начала. После этого часть байтов длиной [le] (length) записываются и затем снова пропускаются байты длиной [xsh] (xshift). Такие le-xsh операции повторяются [n] (number) раз. Новый файл будет иметь ровно [le]*[n] байтов. Эта операция полезна, например, для выделения малой части большой картинки в новую картинку. Она также может быть полезна при работе с числовыми матрицами.
#f [op=fcat; file=any;]
Эта операция возвращает каталог файлов в папке, указанной параметром [file]. Имя папки должно быть указано полностью, начиная от папки интерпретатора. Каталог записывается в текстовый массив t() начиная с индекса, определяемого параметром s(3). После записи, как обычно, s(4) и s(5) указывают начало и длину проведенной записи. Если параметр [file] указывает не на папку, то возвращается s(5)=0. Список задается одной строкой, в которой имена файлов разделяются символом вертикальной черты [|].
#f [op=find; b=; le=; file=any;]
Эта операция выполняет поиск некоторой совокупности байтов внутри файла, имя которого указывает параметр [file]. Последовательность байтов должна быть предварительно определена их значениями в целом массиве i(), причем индекс первого элемента определяет параметр [b] (begin), а число байтов определяет параметр [le] (length). Результат поиска возвращается в параметр s(2) как число повторений данной системы байтов. Важно помнить, что байты имеют значения от -128 до 127 в отличие от ASCII кодов. Например, код
#d 2 i(11) 13 10 #f [op=find; b=11; le=2; file=any;]
покажет число строк внутри файла в системах ДОС и Windows как значение s(2). Если s(2)=0, то этот файл либо имеет одну строку, либо он записан в системе UNIX-оидов, либо он числовой.
#f [op=repl; b=; le=; mo=; file=any;]
Эта операция делает более сложную работу по сравнению с предыдущей операцией. Она находит все копии одной системы байтов и заменяет их на другую систему байтов. Опять все байты, число которых равно значению параметра [le], должны быть заданы в массиве i(), начиная с индекса, определяемого параметром [b]. Но среди них [mo] первых байтов определяют систему, которая заменяется (модифицируется), а оставшиеся [le]-[mo] байтов определяют систему, которая будет записана вместо них. Новое содержание будет спасено в файле с тем же именем. Поэтому, если необходимо сохранить исходный файл, он должен быть предварительно скопирован с другим именем. Например, код
#d 2 i(11) 44 46 #f [op=repl; beg=11; len=2; mod=1; file=some.dat;]
заменит все запятые в файле на точки.
#f[op=line; file=any; n=; b=; le=; emp=;]
Эта операция читает и записывает в файл строки текста. Если параметр [n=-N;] отрицательный, то прочитывается N-я строка текста в файле и ее содержание записывается в текстовый массив t() начиная с индекса, задаваемого s(3). После операции s(4) и s(5), как обычно, показывают начало и размер записанного текста. Если N-й строки в файле нет, то параметр s(5) будет равен нулю. Наоборот, если параметр [n=N;] положительный, то в файле записывается N-я строка, содержанием которой будет часть текстового массива t(), начиная с первого элемента, индекс которого определяется параметром [b] (begin), а число символов равно значению параметра [le] (length). Если файл имел больше, чем N строк, то старое содержание N-ой строки будет изменено. Если в файле было меньше, чем N строк, он будет иметь ровно N строк. Строки между последней из бывших и N-й будут пустыми. Такой режим работы соотвествует старым версиям, когда параметр [emp=0;]. Но если параметр [emp] не равен нулю, например, [emp=1;], то при положительном [n=N;] в файле просто уничтожается N-ая строка. Параметр [emp] (enhanced mode parameter) позволяет реализовывать дополнительные режимы работы команд, но он отсутствует в старых версиях, которые соответствуют нулевому значению. Поэтому не забывайте возвращать параметр [emp] в исходное состояние [emp=0;].
#f [op=list; file=any; c=; b=;]
Эта операция выполняет специальную работу. Она прочитывает файл, имя которого указано параметром [file] и создает специальную строку текста в текcтовом массиве t(). Она берет в этот текст из файла все символы начиная от начала и до символа, ASCII код которого определяется параметром [c] (code), включая и его самого. Затем она пропускает все символы после ASCII кода [c] до тех пор, пока не встретит символ с ASCII кодом [b] (begin), включая и его самого (то есть его тоже пропускает). Затем она снова берет все символы до первого появления ASCII кода [c] и так далее. Это выглядит так, как будто команда берет одну часть из двух частей записей в файл. Если запись имеет имя и тело, то команда может составить список имен. В результате число имен возвращается в параметр s(2), а сформированная строка записывается в текстовый массив t() начиная с индекса, определяемого, как обычно, параметром s(3). После операции номер первого записанного символа и число символов можно получить из параметров s(4) и s(5), аналогично другим похожим операциям. Такую работу, в принципе, можно сделать и с помощью других команд ACL, но эта операция работает намного быстрее, особенно для больших файлов. Как использовать эту операцию в реальной программе каждый программист решает сам.
Описываемые ниже операции реализуют намного более сложную работу, но эта работа делается полностью автоматически, так что использовать операции легко. А все особенности последующей работы программы можно изучить экспериментально, но уже в процессе работы с готовой программой.
#f [op=edit; c=; mo=; file=; b=; le;] FT
Эта операция запускает готовый текстовый редактор для редактирования содержимого файла в отдельном окне. Имя файла, как обычно, определяется параметром [file]. Если файл отсутствует, то он будет создан. В этом случае редактор начинает с пустого текста. Отредактированное содержание будет записано в тот же файл, если [mo=1;] или не будет записано совсем, если [mo=0;]. Второй режим удобен для информационных файлов, содержание которых должно быть только показано и исправления нежелательны. При [mo=1;] файл перезаписывается автоматически при выходе из редактора. Меню редактора имеет три кнопки: [find] [font] [exit]. Кнопка [find] стандартная. Кнопка [font] позволяет выбрать один из 5 логических фонтов и размер фонта. Эти значения хранятся в параметрах [fk] == s(27) (font kind) и [fs] == s(28) (font size). Как обычно, измененные значения параметров будут действовать до следующего определения, причем их можно изменить в любом месте программы. Текущие значение этих параметров запоминаются в файле "start.acl". Логические фонты реально отсутствуют. Интерпретатор проверяет наличие фонтов на компьютере и находит реальные фонты, близкие по свойствам заявленным логическим фонтам. Так что фонты можно просто проверить эмпирически и выбрать подходящий. Параметр [c] учитывается аналогично другим GUI операциям. А именно, если [c] = 0, то программа продолжает выполнение других команд после открытия окна редактора. Раньше это использовалось для автоматической презентации с помощью команды #robot. Сейчас команда #robot заблокирована, но режим сохранен, однако использовать его нужно с осторожностью. Если [c] = 1, то программа ждет когда пользователь закроет окно. Позиция курсора при входе в редактор определяется параметром s(1) без имени. После выхода из редактора этот же параметр содержит позицию курсора перед выходом. Позиция задается номером символа в тексте начиная от начала. Первый символ имеет индекс 0. Знание позиции курсора может быть использовано, например, при чтении книг, так как позволяет запомнить место остановки чтения, и в следующий раз начать прямо с этого места. Заголовком окна является полное имя файла. Существует специальный режим работы команды, который соответствует [file=here;]. В этом случае редактор работает с частью текстового массива t() вместо файла. Эта часть начинается с индекса, определяемого параметром [b] и имеет [le] символов. При этом параметр [mo] должен иметь значения -1 или -2. При [mo=-1;] результат редактирования не записывается (только чтение), а при [mo=-2;] отредактированный редактором текст записывается снова в текстовый массив, начиная с индекса, определяемого параметром s(3). Как обычно, после записи параметры s(4) и s(5) показывают начало и длину записанного текста. В этом случае заголовок окна задается аргументом в виде форматированного текста. Аргумент обязателен и его отсутствие фиксируется как ошибка. В новых версиях интерпретатора, редактор имеет дополнительные разделы меню и встроенное описание (Help).
#f[op=html; file=any;]
Эта операция дает возможность программисту показывать html-файлы, то есть запускать простой браузер. Имя файла определяет параметр [file], но в данном случае имя должно быть указано полным относительно папки интерпретатора, рабочая папка не используется. Гиперссылки внутри файла показываются в отдельном окне, которое накладывается на предыдущее, поэтому для того, чтобы вернуться назад, нужно просто закрыть текущее окно, или переместить новое окно в другое место. Размеры окна в этой и в предыдущей операциях заданы стандартным образом, но пользователь может их изменить по своему вкусу с помощью мышки (передвигая границы). Браузер выполнен в самом простом варианте и не содержит ни меню, ни иконок. Он предназначен, в основном, для показа специально приготовленных информационных текстов, вся навигация в которых может быть организована с помощью гиперссылок.
#f [op=choo; mo=; file=;]
Эта операция запускает специальную программу, называемую на языке Java именем FileChooser. FileChooser дает возможность пользователю просмотреть полную файловую структуру используемого компьютера аналогично Менеджеру файлов в Solaris OS или программе "My Computer" в Windows OS. Просмотр начинается с папки, указанной параметром [file]. Параметр [file] может указывать или на любой файл, отсчитываемый от главной папки интерпретатора или сразу на папку. Для указания главной папки, то есть папки интерпретатора, можно использовать [file=start.acl;]. Программа имеет два варианта. Если [mo=0;], то используется общая форма и показываются все файлы. Кнопка [Select] позволяет выбрать файл. Полное имя выбранного файла, отсчитываемое от папки интерпретатора будет записано в текстовый массив t(), начиная с элемента согласно значению параметра s(3). После операции, как обычно, s(4) и s(5) показывают начало и длину записи. Пользователь, в принципе, имеет возможность выбрать файл, находящийся за пределами папки интерпретатора. Но в этом случае операция вернет пустое имя и параметр s(5)=0. В любом случае программист должен проявлять осторожность и проверять наличие выбранного файла с помощью операции [op=size;] в добавление к проверке значения s(5) прежде чем выполнять какую-либо операцию с выбранным файлом. Если [mo=1;], то используется специальная форма, в которой показываются только графические файлы с предварительным просмотром. После выбора файла его имя будет передано так же как и описано выше. Выбранное имя программист может использовать по своему усмотрению.
Следующие операции относятся к команде файл по той причине, что они реально создают файлы. Но эти файлы имеют специальную природу. Эти операции могут пригодиться только в программах по обработке изображений. Поэтому читатели, которых не интересует эта тема, могут пропустить изучение этих операций. Тем более, что здесь используется специальная информация об изображениях. Кроме того, эти операции реализованы только на ПК, на КПК их нет. Как правило, эти операции используются в блоке с другими командами. После такого введения все же приступим к работе.
#f [op=tobm; file=; form=; dir=; n=; col=;]
Эта операция конвертирует графические файлы с расширениями (gif, jpg, png) в byte-map файлы. Каждый байт такого файла описывает уровень серого от 0 до 255. Параметр [file] должен указывать имя графического файла, например, [file=one.jpg;]. Однако, если [file=here;], то картинка будет считана не из файла, а из оперативной памяти, в которой хранится массив картинок, как элемент с номером [n]. Такая картинка должна быть предварительно записана. В результате операции будет создан новый файл с именем, заданным параметром [form] как графический файл в новом формате. Этот формат нестандартный, некоторые его называют raw (необработанный). При этом, параметр [dir] (direction) определяет направление записи по вертикали. Если [dir=1;], то запись идет в направлении сверху вниз, обычном для графических файлов. Для любого другого значения, например [dir=0;], картинка записывается начиная с нижней строки вверх, как это делается во всех расчетных изображениях. Как обычно, элементы строк картинки записываются подряд. Ширина картинки возвращается в параметр s(12), а высота -- в параметр s(13). Цветные картинки преобразуются в серые по закону скалярного произведения с заданным цветом, номер которого в массиве цветов задает параметр [col]. Пусть этот цвет имеет компонетны R,G,B. Тогда цвет каждого пиксела приводит к уровню серого g по закону g=(r*R+g*G+b*B)/255), но внутри интервала (0,255). Такой способ позволяет применять цветные фильтры при конвертировании картинки, а также менять ее яркость. Информация о цветах будет описана ниже в разделе описания графики. Размер записанного файла в байтах равен числу пикселей в картинке.
#f [op=tocm; file=; form=; dir=; n=;]
Эта операция конвертирует графические файлы с расширениями (gif, jpg, png) в color-map файлы. Каждые три байта такого файла описывают уровень красного, зеленого и синего от 0 до 255 для одной точки рисунка (пиксела). Параметр [file] указывает имя графического файла, например, [file=one.jpg;]. Однако, если [file=here;], то картинка будет считана из оперативной памяти из массива картинок как элемент с номером [n]. Такая картинка должна быть предварительно записана. В результате будет создан новый файл с именем, заданным параметром [form] как графический файл в новом формате. При этом, параметр [dir] определяет направление записи по вертикали. Если [dir=1;], то запись идет в направлении сверху вниз, обычном для графических файлов. Для любого другого значения, например [dir=0;], картинка записывается начиная с нижней строки вверх, как это делается во всех расчетных изображениях. Как обычно, элементы строк картинки записываются подряд, три цвета на каждый элемент. Ширина картинки возвращается в параметр s(12), а высота в параметр s(13). Размер записанного файла в байтах равен утроенному числу пикселей в картинке.
#f [op=tops; mo=; uni=; file=; form=; n=;]
Эта операция конвертирует графические файлы с расширениями (gif, jpg, png) в EPS файлы (encapsulated postscript file). Параметр [file] должен указывать имя графического файла, например, [file=one.jpg;]. Однако, если [file=here;], то картинка будет считана из оперативной памяти из массива картинок как элемент с номером [n]. Такая картинка должна быть предварительно записана. В результате операции будет создан новый файл с именем, заданным параметром [form] как графический файл формате eps. Имя файла указывается вместе с расширением. Оно не обязано иметь расширение eps. При этом параметр [mo] должен быть 0 или 1. Значение 0 приводит к созданию черно-белого eps-файла, а значение 1 к созданию цветного eps-файла. Другие значения приводят к ошибке. Параметр [uni] определяет дополнительно масштабирующий фактор для картинки, чтобы получить соответствие размеров eps-картинки на экране исходной картинке. Этот фактор равен отношению пиксела экрана к PS точке в единицах 0.0001. Например, мой DELL Latitude ноутбук имеет 1024 пиксела при размере экрана 28.42 см. Когда я использую GhostView в режиме увеличения, показывающем бумагу формата A4 (595 точек) на 26.4 см, отношение будет 0.6255. С этим значением GhostView на моем компьютере показывает картинку точно также, как и vkACL. Однако, это соотношение может другим на других компьютерах. Каждый пользователь может определить отношение эмпирическим путем. В моем случае [uni=6255;]. Иногда очень важно иметь соответсвие каждого пиксела каждой точке экрана так как интерполяция приводит к артифактам. Здесь уместно отметить, что полная версия интерпретатора выполняет команду #pd, которая позволяет конвертировать большой спектр графических форматов в pdf-формат. Это делается специальной библиотекой Java-классов, написанной другими авторами. А данную операцию я реализовал сам.
#f [op=topng; form=; n=;]
Эта операция конвертирует графические картинки, записанные предварительно в памяти компьютера, в файл png-формата. Запись картинок в память компьютера проводят графические команды #w #g #eg . Сами эти команды способны конвертировать картинки в файл jpg-формата. Но jpg-формат не всегда подходит для линейной графики. Файлы получаются неоправданно больших размеров, а линии окружены заметным грязным фоном, обусловленным алгоритмом jpeg-сжатия. В этом случае разумно спасать созданные картинки в память и затем конвертировать в png-файл с помощью указанной операции. Здесь параметр [form] указывает имя файла без расширения, который будет записан. Расширение всегда ".png". Параметр [n] указывает номер картинки, спасенной предварительно в памяти. Если картинка не существует, будет выдано сообщение об ошибке. Эта операция сделана для универсальности. Так как картинки создают несколько команд, то реализация в каждой команде записи в файлы разных форматов приводит к увеличению кода и сложной структуре параметров.
8. ОБЩЕНИЕ С ПОЛЬЗОВАТЕЛЕМ
Любая программа для своей работы должна получать данные от пользователя. Эти данные обычно разделяют на два типа. Данные первого типа условно назовем таблицами. Это, как правило, система чисел, слов, картинок, имеющая некоторую структуру, иногда достаточно сложную, и записанная в файлы. Данные второго типа условно назовем инструкциями. Это указания программе какие вычисления надо проводить, какие таблицы записать и что нужно делать с уже имеющимися таблицами. В первых языках программирования, написанных для компьютеров без экрана, инструкции также записывались в файл, либо в саму программу, либо в специальные текстовые файлы. Такой способ и сейчас используется и даже иногда является достаточно удобным. Например, одна программа может генерировать таблицы и одновременно инструкции для другой программы, которая работает сразу за ней. Вторая программа прочитывает инструкции, преобразует таблицы и точно так же передает весь арсенал третьей программе. В этом случае пользователь участвует только в самом начале, после чего программы общаются между собой автоматически.
Но существуют и другие программы, которые полностью зависят от пользователя и запрашивают у него данные. Например, программа, собирающая базу данных для подбора знакомств между юношами и девушками. В таких программах обычно реализуют интерактивный режим, в котором пользователю что-то сообщают, просят сделать выбор или ввести информацию в заготовленные формы. Поэтому программы, работающие в оконных операционных системах, имеют определенный арсенал средств для удобного графического общения с пользователем. В языках низкого уровня есть возможность создавать самим средства такого типа, хотя это и довольно громоздкая работа. Что касается ACL, то в нем таких средств пока немного, но со временем будет больше. Тем не менее самые необходимые средства все же есть. Их мы как раз и обсудим в этом разделе. Первая команда такого типа уже рассмотрена нами в разделе 3. Это #m (message). Она показывает форматированные тексты с определенной информацией и иногда просит пользователя сделать выбор, нажав на определенные кнопки. К операциям общения с пользователем относятся также операции edit, html и choo команды #f (file). Первая из них просит набрать текст в редакторе, вторая показывает информацию в виде форматированного html-текста со ссылками, а третья просит выбрать файл, одновременно показывая какие файлы есть на компьютере. В этом разделе мы рассмотрим еще две команды такого типа. Первая команда #select или
#sel [nx=; ny=; mo=; wid=; hei=; tsi=; col=; emp=; xsh=; ysh=;] FT FT.
Эта команда, создает новую панель, показывающую квадратную таблицу кнопок. Число кнопок в одном ряду задается параметром [nx], а число рядов - параметром [ny]. Команда предлагает пользователю сделать выбор одного варианта из многих вариантов. Панель имеет заголовок, который определяется первым аргументом FT. Второй аргумент FT содержит имена всех кнопок. Имена внутри FT разделяются символом вертикальной черты [|]. Имена кнопок сначала заполняют первый ряд, затем второй и т.д. Есть несколько модификаций работы программы. Если [mo=1;], то кнопки задаются картинками из файлов с расширением jpg, которые ищутся в текущей папке. Не забудьте правильно определить текущую папку перед использованием команды. В этом случае именами кнопок являются имена файлов, но без расширения. Например, "run" означает файл "run.jpg" в текущей папке. Если [mo=0;] именами всех кнопок являются тексты, которые показываются внутри прямоугольника некоторого размера, окрашенного в некоторый цвет. В этом случае определение параметра [col=j;] означает, что j равно индексу элемента массива цветов, который задает номер цвета текста (стандартно 255), следующий элемент этого массива задает цвет фона (прямоугольника). Массив цветов может быть переопределен командой #color. Параметры [wid] и [hei] определяют размер прямоугольника в пикселах экрана. Параметр [tsi] задает размер текста.
Когда пользователь кликает одну из кнопок, панель исчезает и переменная [&] становится равной номеру выбранной кнопки. Программист может использовать эту информацию по своему усмотрению. Как правило, после этой команды стоит серия условных команд #case .... #end | , которая обрабатывает каждое значение переменной [&] из нужного диапазона.
Раньше таблица кнопок устанавливалась автоматически в середине экрана. Затем был введен новый параметр [emp] (enhanced mode parameter), стандартное значение которого всегда равно нулю. В этом случае действует старый режим для совместимости с ранее написанными программами. Если его значение отлично от нуля, то положение таблицы кнопок определяется параметрами [xsh] и [ysh], а именно, они задают смещение верхнего левого угла панели кнопок от верхнего левого угла экрана. При изменении параметра [emp] не забывайте обнулять его после выполнения команды.
Здесь полезно проинформировать вперед, что для реализации такого запроса можно использовать и более сложный режим. Команда #w (window) позволяет выставить на экран картинку произвольного содержания в виде одной кнопки. Когда пользователь кликает картинку, программа возвращает позицию курсора в момент клика. На картинке можно изобразить серию кнопок в произвольной конфигурации, например, в виде таблицы Менделеева, а по позиции курсора определить номер нажатой кнопки. Но такой режим требует написания сложного кода на ACL, в то время как команда #sel все делает автоматически.
Вторая полезная команда #inp (input) создает новую панель, на которой в определенном порядке расположены окна ввода с комментариями.
#inp [n=; le=; mo=;] FT FT FT ...
Каждое окно ввода позволяет ввести одну строку текста. Число строк определяется параметром [n]. Простейший случай [n=1;] с одним окном ввода. В этом случае панель имеет заголовок, определяемый первым аргументом FT. Комментарий к окну задается вторым аргументом FT. Наконец, третий аргумент FT содержит начальный текст, который будет введен в это окно ввода. Но если [n] > 1, то возможны две модификации задания комментариев и текстов. Если [mo] - четное число (0,2,...), то 2-й аргумент FT определяет первый комментарий, 3-й аргумент FT определяет первый текст, 4-й аргумент FT определяет второй комментарий, 5-й аргумент FT определяет второй текст и так далее. Так что в этом случае каждой аргумент FT определяет один комментарий или текст. В противоположном случае, если [mo] - нечетное число (1,3,...), то 2-й аргумент FT определяет все комментарии, отделяемые один от другого символом вертикальной черты [|], а 3-й аргумент FT определяет все тексты отделяемые каким же способом.
Есть также несколько модификаций размещения комментариев и окон ввода на панели. Если [mo] = 0,1, то каждый комментарий и каждое окно ввода размещаются на новой строке сверху вниз. В этом случае комментарий помещается над окном ввода. Если [mo] = 2,3,4,5, то каждая пара комментарий+текст будут помещены на одной строке, образуя один столбец. Если [mo] = 6,7, то пары комментарий+текст будут помещены в два столбца. В этом случае параметр [n] должен иметь четное значение и первая половина окон ввода попадет в первый столбец, а вторая -- во второй. Если [mo] = 8,9, то пары комментарий+текст будут помещены в три столбца. В этом случае параметр [n] должен делиться на три и первая треть окон ввода попадет в первый столбец, вторая -- во второй и третья -- в третий. Если [mo] > 9, то окна ввода будут размещены по 4,5,.. в строке, но столбцы при этом не выравниваютя. Последний режим я еще ни разу не использовал.
Нижняя линия панели содержит две кнопки [OK] и [Cancel]. Параметр [le] определяет длину пустого пространства с обоих сторон от этих кнопок. Это позволяет задавать длину окон ввода, так как комментарии записываются целиком, а окна ввода не имеют размер и их длина зависит от общей ширины панели, определяемой по другим параметрам. В частности, если [mo=0;] или [mo=1;], то ширина панели зависит от максимальной длины заданных строк. С помощью параметра [le] можно сформировать любую ширину. Обычно [le] = 50, но может быть больше или меньше. Значение [le] оптимизируется эмпирически. Панель всегда помещается в центре экрана.
Содержание всех окон ввода можен быть изменено пользователем. После выхода из диалога новые тексты будут помещены в текстовый массив t() аналогично команде #print. Содержание разных окон ввода разделяется ASCII кодом 10 (разделителем строк). Напомню, что индекс первого элемента для записи в массив символов t() определяется параметром s(3). После операции параметр s(4) указывает на начальный индекс первого элемента записи в t() с новым текстом (s(3) будет уже другим) и s(5) покажет длину всех записей включая и код 10 после каждой строки. Поэтому соблюдайте осторожность в работе с введенным текстом. Например, в случае [n=1;] его длина будет s(5)-1.
Панель содержит две кнопки [OK] и [Cancel]. Если пользователь выбирает кнопку [Cancel], то текстовый массив t() не будет заполнен, а переменная [&] будет равна 2. Текстовый массив будет записан только если нажата кнопка [OK]. В этом случае [&] будет равен 1. Расположение кнопок тоже зависит от модификации. Если [mo] = 0,1 или > 5, то они расположены в центре и [le] задает пустое пространство с обоих сторон. Если [mo] = 2,3, то они расположены в левой стороне, а все пустое пространство длиной 2*[le] справа. Если [mod] = 4,5, то они расположены в правой стороне, а все пустое пространство длиной 2*[le] слева.
9. ЧТО ЕСТЬ ЕЩЕ
Есть специальная команда для работы с zip-архивами, есть команда на простую графику, а также команды позволяющие показывать готовые графические модули. Есть еще дополнительная команда расширенной графики, дающая практически все возможности для рисования произвольного рисунка и обработки изображений, полученных, например, цифровым фотоаппаратом. Есть также команда на запись и воспроизведение звука, есть команда на анимацию, то есть показ картинок с автоматической сменой кадров любым фасонным образом. Есть команда #system для запуска внешних готовых программ и других системных операций. Важное значение имеет команда математики, имеющая много операций и позволяющая делать различные математические процедуры над матрицами и массивами чисел больших размеров непосредственно в коде компьютера и поэтому быстро. Есть команда для операций с текстовым массивом t().
Наконец, реализован аппарат создания и работы с суперкомандами. Суперкоманда -- это готовая процедура, которая записывается в файл (можно и с предварительной инструкцией) и которая может считывать аргументы из параметров или из текстового массива t(). Этот файл помещается в специальную папку с названием [proc]. После этого соответствующую процедуру из файла можно вызывать по имени файла (без расширения) поставив перед именем два знака [##]. Удобство такого подхода в том, что один раз определенная процедура может использоваться многократно в разных ACL программах, а ее вызов похож на запуск обычной команды.
Все возможности языка подробно описаны в более полном описании на русском языке. Перевод на английский язык сейчас легко делает Гугл и Яндекс. Там есть статьи по каждой команде отдельно и статья по некоторым методам программирования. Недавно я сделал специальную версию интерпретатора, которая работает с готовыми ACL-файлами как обычный проигрыватель. У нее есть серия преимуществ. Во-первых, соответствующий jar-файл можно переименовать на любое имя. Во-вторых, оболочка программы, в частности меню главного окна можно настроить по специальному протоколу. Ну и размер этой программы намного меньше, так как не содержит средств отладки, в частности встроенных описаний языков и различных демонстраций.
Очевидно язык можно наращивать бесконечно. Я использовал многое из того, что в готовом виде дает Java и что у меня было разработано за всю жизнь. Можно еще добавлять математические процедуры и процедуры создания интерфейса. Пока нет команды работы с медиа-файлами, хотя это тоже возможно. Есть еще один канал для развития -- накопление банка готовых программ на языке ACL. Дело в том, что уже есть много ACL-программ, которые можно эффективно использовать, вообще не зная языка ACL и эти программы встроены в оболочку. В частности есть очень развитый калькулятор с 8 ячейками памяти, позволяющий считать по формулам и запоминать много вариантов уже набитых формул для повторного расчета. Есть команда #pd, которая реализует возможность создания и работы с pdf файлами. Это очень сложная работа и для ее реализации я использовал библиотеку классов Java с названием "iText", которая бесплатно предлагается в интернете. Я убежден, что программа заслуживает того, чтобы выучив язык ACL использовать ее в работе. Я сам пользуюсь ей каждый день для выполнения многих работ и она мне очень помогает.
Для любопытных я покажу здесь текст программы калькулятора.
-----------------------------------------------------
#pro main #f [op=fold; file=0;] # &=1; !
#case 1 # s(3)=1; #pr 01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|\E
. #sel [nx=9; ny=3; mo=0; wid=30; hei=25; tsi=16; col=255;] Select variant\E \T1 s(5)\E # S=&; &=S<2; !
. #case 1 #p [file=calcul.inf;] #f [op=edit; mo=1; c=1;] # Q=3; #end |
. #case 2 # a=0; b=0; c=0; d=0; e=0; f=0; g=0; h=0; #e _inpc # &=Q; !
. . #case 1 #m [op=uni;] Would you like to continue ?\E Continue|This|Other|No\E # Q=&+1; !
. . #end | # &=Q; !
. #end # &=Q-2; !
#end #f [op=fold; file=oldf;]
@
#pro inpc # &=(S-1)<1; !
#case 1 # s(3)=1; #f [op=line; n=-S+1; file=calcul.txt;] #p [err=1;] #%
. #case 10101 #m [op=win;] \n\U32*5;Sorry, you have typed\n\U32*5;the invalid expssion.\n\U32*5;Please try again.\n\E #end |
. # s(3)=1; #inp [n=8; le=80; mo=3; ysh=70;] Calculator\E a = |b = |c = |d = |e = |f = |g = |h = \E \T1 s(5)\E # Q=&; !
. #case 2 # Q=Q+1; s(26)=0; #end | # &=Q; !
. #case 1 # z=s(5); s(3)=3999; #te [op=repl; b=1; le=z; c=10; mo=124;]
. . #e _make #e _text #p [err=0;] #f [op=line; n=S-1; b=1; le=z;]
. . #m [op=win;] \n a =\G a;\n b =\G b;\n c =\G c;\n d =\G d;\n e =\G e;\n f =\G f;\n g =\G g;\n h =\G h;\n\E
. #end |
#end |
@
#pro make #te [op=find;b=1;le=z; n=1; cod=124;]
# A=1; B=i(1)-A; C=i(1)+1; D=i(2)-C; E=i(2)+1; F=i(3)-E; G=i(3)+1; H=i(4)-G; I=i(4)+1; J=i(5)-I; !
# K=i(5)+1; L=i(6)-K; M=i(6)+1; N=i(7)-M; O=i(7)+1; P=i(8)-O; !
#pr \#\U32;a=\TA B ; b=\TC D ; c=\TE F ; d=\TG H ; e=\TI J ; f=\TK L ; g=\TM N ; h=\TO P ; \#p\U32;\[\]\U32; \E
#p [b=s(4); le=s(5);]
@
#e _main !
-----------------------------------------------------
Эта программа использует многое из того, о чем написано выше. Используется рабочая папка с именем 0. Сначала программа показывает таблицу кнопок размером 9*3 с именами вариантов расчетов. Номер [01] особенный, при его выборе запускается текстовый редактор для редактирования файла с именем "calcul.inf" в рабочей папке, в котором записана инструкция для пользователя и памятка на варианты. При выборе других номеров запускается процедура ввода формул с контролем возможных ошибок. Для этого используется параметр [err] и команда #%. Введенные формулы записываются в текстовый массив и динамически выполняются как процедура. При обнаружении ошибки программа снова возвращается на ввод формул. Если же ошибок нет, то все 8 переменных получают нужные значения, которые показываются в новом окне сообщения в виде текста. В последующем запрашивается ответ на продолжение. Можно выбрать тот же самый вариант, другой вариант или отказаться. Каждый раз формулы в окнах считываются из файла "calcul.txt" , а при записи новых формул снова записываются в этот же файл. Для правильной работы программы указанные файлы должны существовать, иначе будет зафиксирована ошибка.