Работа с файлами в Dyalog APL

    Всем привет. Сегодня я расскажу (и покажу) вам, как организована работа с файлами в Dyalog APL. Экспериментировать будем с «родной» файловой системой Dyalog – DCF, остальные файлы в данной статье не рассматриваются. Также будет затронута тема обработки исключений. Все опыты будут проводится на материале из прошлого топика. К сожалению, некоторые изображения хренового качества, прошу меня извинить, ибо не знаю хорошего хостинга картинок. Итак, поехали.


    Что такое DCF?


    В Dyalog APL существует свой способ хранения информации – компонентные файлы. Суть такова:
    файл – это последовательность «ячеек» (компонент), причем в каждую из них можно записать значение только одной переменной. Над файлом можно проводить следующие операции: создать, открыть, считать компоненту, заменить компоненту, присоединить новую компоненту в конец, закрыть и различные специальные операции, в данной статье не рассматриваемые.
    Попробуем открыть какой-нибудь файл:

    image

    И получаем ошибку, ведь файла то нет. Из этого «эксперимента» можно сделать 2 вывода:
    — для открытия файла используется системная функция []FTIE. Её аргументами являются полное имя файла слева и специальный номер-идентификатор файла (tie number), который в дальнейшем используется для обращения к файлу;
    — сообщения об ошибках выдаются в окно интерпретатора с указанием причины ошибки (в нашем случае это FILE NAME ERROR). Немного забегая вперед отмечу, что у каждой ошибки есть свой номер, что используется для перехвата этих самых ошибок.
    Итак, открыть файл не получилось. Значит надо его создать! Для этого используется системная функция []FCREATE с такими же аргументами, что и []FTIE.

    image

    Ну вот, теперь у нас есть файл test.dcf с идентификационным номером 1. По умолчанию (если не указать полный путь) файлы появляются на рабочем столе. Созданные файлы не содержат компонент, в чем можно убедиться при помощи системной функции []FSIZE, передав ей номер файла справа.

    image

    Нас пока интересует только второй элемент результата этой функции – он содержит номер следующей свободной компоненты. Так как для файла test.dcf этот номер равен 1, то число компонент равно нулю.
    Для заполнения файлов используется системная функция []FAPPEND, которой необходимо передать переменную или значение слева и номер файла справа. Чтобы проверить, что информация действительно записалась, воспользуемся функцией []FREAD. Она принимает один аргумент из двух значений – номера файла и номера компоненты.

    image

    Для закрытия файла используется функция []FUNTIE с номером файла. Чтобы убедиться, что связи с файлом больше нет, воспользуемся nyladic функцией []FNUMS, которая возвращает вектор номеров файлов.

    image

    Используя полученные знания попытаемся модифицировать программный код из предыдущего примера.

    Немного практики


    Внесем в программу несколько изменений:
    1. Запишем в файл, использующийся в программе текст и будем загружать его при выполнении.
    2. Запишем в другой файл тексты функций и будем выполнять их при загрузке рабочей области, а при выходе – сохранять обратно в файл.

    Сохранение и загрузка текста

    Сначала напишем функцию, которая будет осуществлять инициализацию текста программы – txtIni. Нам необходимо: открыть файл, если файла не существует то создать его и записать текст, считать текст в глобальную (для данной функции) переменную и закрыть файл.
    Заголовок функции будет выглядеть так:

    image

    Функция принимает полное имя файла в качестве аргумента (fname). Переменная tien будет хранить номер файла.
    При работе с файлами легко допустить ошибку, поэтому необходимо воспользоваться инструментами обработки ошибок. Для нашего примера подойдет конструкция :Trap :Else :EndTrap, которая несколько похожа по идеологии на try-catch.

    image

    Конструкция :Trap 0 1000 определяет коды ошибок, которые должны перехватываться. В данном случае это все системные события (0) и все пользовательские (1000). Также внутри этих границ можно определить обработку отдельных событий при помощи конструкций :Case и :CaseList. Обратите внимание на применение метки L1: — она используется для безусловного перехода после обработки исключения. Остальное просто: если файл содержит компоненты, читаем первую в переменную txt, если нет – заполняем её текстом и пишем в файл. Затем файл закрываем.
    Далее необходимо определить последовательность действий при возникновении ошибок. Для отсутствия файля пишем отдельный :Case, для остальных — :Else. Самое простое, что можно сделать при непредвиденных ошибках – это закрыть все открытые файлы функцией []FUNTIE с аргументом []FNUMS, а затем выдать сообщение в окне интерпретатора при помощи функции []SIGNAL. Её аргументами являются текст сообщения справа и код ошибки слева. В нашем примере достаточно будет воспользоваться системными переменными []DM(текст) и []EN(код возникшей ошибки). При отсутствии файла (или неверно указаном имени) этот файл создается и затем осуществляется переход на метку. Всё очень просто!

    image8

    Чтобы загружаемый текст использовался в программе, необходимо немного изменить функции приложения, добавив обращения к элементам текстового вложенного вектора txt.

    image9

    В функции WhatNum был убран аргумент, так как его значение(заголовок формы) теперь берется из файла. Все символьные константы были заменены на обращение к элементам вектора txt с вызовом функции «Disclose», которая преобразует вложенный элемент вектора в символьный вектор. В функции checkNum изменения те же:

    image10

    Прячем код

    А теперь самое интересное: в компонентных файлах можно хранить переменные, но можно ли хранить функции? ДА! Для этого нужно только преобразовать функцию в текст и присвоить его переменной, что и будет делать функция saveFns.
    Преобразование делается моментально: системная функция []CR (canonical representation) с именем функции в качестве аргумента возвращает символьную матрицу с текстом. Но настоящая магия начинается с применением оператора «Each».

    image11

    Таким образом, переменная all содержит три текста наших функций причем каждый текст представляет собой символьную матрицу, представленную в виде скаляра(ранг равен 0)!
    Далее следует уже описанная процедура записи в файл. В конец добавим вызов системной функции []EX, которая удалит из рабочей области ненужные объекты.

    image12

    Теперь осталось написать код начальной инициализации, который и будет выполняться при загрузке рабочей области. Последовательность действий такая: открытие файла, чтение, закрытие файла. Далее опять начинает работать магия APL: легким движением руки вектор all превращается в полноценные функции рабочей области! После загрузки функций можно вызвать WhatNum и убедиться, что всё работает.

    image13

    Отлично. Осталось только сохранить наши функции в файл, изменить содержание системной переменной []LX, и сохранить рабочую область. После этого в рабочей области будут находится только функции для работы с файлами, а код приложения будет загружаться.

    image14

    Для запуска программы достаточно открыть файл рабочей области, и если всё сделано правильно, окно приложения появится на экране.

    Итого


    Cегодня вы увидели немного магии APL, а заодно узнали о таких важных вещах как работа с файлами и обработка исключений. Продолжение следует.

    Комментарии 2

      0
      Можно посмотреть код не в виде скриншотов?
        0
        Можно, только весь специальный синтаксис APL не будет отображаться, например
        {⎕IO←1 ⋄ {⍵-¯1↓0,⍵}(((1↓⍵)≠¯1↓⍵),1)/⍳⍴⍵}

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое