| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
управление с помощью оператора вызова подпрограмм Do (а не Go или If, которые
просто передают управление строке с указанным в них номером) группа, по
достижении её конца, вернёт управление в точку вызова (следующему после Do
оператору) даже в том случае, если там в ней небыло оператора возврата из
подрограммы Ret.
Заметим, что "естественной подпрограммой" является так же и каждая
отдельная строка, и тело цикла — остаток строки после оператора For.
Чтобы вызвать подпрограмму-строку, адрес перехода в операторе Do должен
быть дробным числом, а чтобы всю группу — целым. Впрочем, так и во всех
остальных операторах, имеющих дело с программными строчками. Если же надо
указать всю программу целиком, это делается с помощью ключевого слова Ales
(что собственно и значит "всё".) И, кстати: везде, где по смыслу нужен номер
строки, может быть вовсе не константа (как в большинстве реализаций всё того
же Бейсика), а как и везде, где по смыслу нужно число — выражение произвольной
сложности. Получается в том числе "вычисляемый переход"...
Пользуясь тем, что каждая строка — тоже естественная подпрограмма, можно
при отладке запускать строчки поштучно и после каждой смотреть что она сделала.
Если же в ней есть передача управления, то чтобы это отслеживать, есть режим
"трассировки" — включается (и выключается) оператором ? (вопросительный знак).
При которой выполнение оператора сопровождается выдачей его на терминал.
Оператор цикла сконструирован так, чтобы помещался в одной (в том числе
"нулевой") строке. Но ежели остатка строки под тело цикла мало — его можно
продолжить в другой строке, передав туда управление оператором Go. После её
завершения, управление опять вернётся заголовку цикла. (Ну или выделить под это
целую группу, передав туда управление оператором Do.) А вот досрочно выйти из
цикла с помощью оператора Go, как в других языках — не получится. Для этого
надо присвоить параметру цикла значение больше конечного. Или сразу
конструировать цикл по-другому: из операторов условной и безусловной передачи
управления. (Ну или применить "структурный переход" — вещь из второй части...)
Еще одно важное различие с Бейсиком: простая переменная и там и там
заводится одинаково: автоматически — при первом её упоминании. А вот
переменные с индексами (они же элементы массивов) — уже нет: в Бейсике массив
существует весь сразу целиком и для этого должен быть объявлен заранее
оператором DIM. При чем индексы в нём не как у всех нормальных людей — с нуля,
а почему-то с единицы! (Надо ли это так понимать, что рассчитано на обывателя?)
А вот в Фокале ничего и никогда заранее не объявляется. Переменная, что
простая, что с индексами одинаково заводится в индивидуальном порядке в момент
первого присваивания ей чего либо. Это не эффективно, за то удобно. И сам собою
отпадает вопрос о границах массива. Но это таки вызывает некоторые
дополнительные сложности: к одной и той же переменной можно обратиться с
помощью разного количества индексов, что еще и зависит от реализации. Впрочем
гарантируется, что нулевое значение индекса эквивалентно его отсутствию. Так
что и Y(0,0) и Y(0) это одна и та же переменная Y.
((Потому что сделано до ужаса просто: под имя и индекс — по два байта.
(Маловато? Потом побольше сделаем...) И как туда попали числа — нигде не
фиксируется! Так что индексы — не просто целые, а если число большое, оно еще
и обрезается... Была еще идея: сделать не только Y[i] или Y[i,j] но и Y.abcdef
чтобы первые две буковки тоже туда попадали, а остальные игнорировались. Но
вот пока не...
Да, это нам не Питон... (Или например Луа.) Где массив это набор пар:
ключ-значение. При чем и то и другое могут быть какого угодно типа -
полиморфизм, однако! Так что у них там даже переменных как таковых нет. Вместо
них — элементы массива: в качестве ключа — строка в роли имени, а значение
элемента — хоть число, хоть тоже строка, хоть другой массив, хоть выполняемый
код — подпрограмма получается... Но неэффективно. Так что настоящие массивы с
пронумерованными элементами у них там вроде бы тоже есть... Но Питон да и Луа
это языки третьего поколения, весьма сложные как грамматически, так и в
реализации. А Фокал — первого. И очень-очень-очень простой.))
Уничтожить переменные, освободив занятую под них память, можно только все
разом — оператором Eraze без аргументов. (Или им же с ключевым словом Ales, но
тогда уничтожается вообще всё...)
И наконец сами ключевые слова: в Фокале все они подобраны на разные буквы
алфавита. В результате чего их можно сокращать до одной только первой буквы.
(Остальные всё равно игнорируются.) Чем (вместе с отсутствием "шумовых" слов) и
достигается лаконичность Фокала: если в Бейсике в строке помещается максимум
три-четыре оператора, то в Фокале — пять-десять. ("Шумовые" слова нужны чтобы
операторы могли косить под фразы "естественного" языка. Впрочем, такого же
примитивного...)
Если в операторе есть второе ключевое слово (вот это самое Ales например),
то к нему это тоже относится. А ежели в операторе может быть как ключевое
слово, так и число, вернее вычисляющее его выражение, которое тоже может
начинаться с этой буквы, то его всегда можно заключить в лишние скобки.
Имена переменных — какие угодно, но значимы только первые два символа.
Остальные — чисто для красоты, тоже игнорируются. Впрочем, калькулятору этого
более чем достаточно. И тоже приучает к лаконичности. Для сравнения: в Бейсике
изначально значащим был только один первый символ имени.
Имена встроенных функций опознаются по первым уникальным буквам и все
обязательно называются на букву Ф. А вот переменные на букву Ф называть нельзя,
увы. (Можно, но использовать её потом не получится.) То есть не sin, tg, sqrt,
а FSIn, FTG, FSQrt... А чтобы не запоминать, как правильно: например FTAN или
FTG — сделано чтобы и так и так.
Символы в Фокале делятся на три категории: буквы, цифры и разделители.
При чем буквой считается всё что не цифра и не разделитель, которых Фокал
знает "в лицо и по фамилии". В том числе буквами считаются: # $ ~_ @ & | :
Но ведь буквы еще бывают заглавные и строчные, а так же русские и
латинские. Не припомню как там было в других реализациях, а я сделал чтобы в
именах переменных все они различались, а в ключевых словах и именах функций
были эквивалентны.
А строку, начинающуюся вот с такой якобы-буквы, повелел считать
комментарием. Наряду с начинающейся с оператора Coment (вернее с буквы Ц).
Потому что в UNIX`е есть такая полезная традиция: распознавать тип файла не по
"расширению" (часть имени после точки), а по "сигнатуре". Каковая — первые два
(четыре, или нынче уже бог весть сколько) символов самого этого файла. В том
числе, в случае если файл — выполняемый, пользователю, какой на самом деле тип
этого файла и как его будет выполнять UNIX — без разницы. А вот для самого
UNIX`а... Если файл не в машинных кодах, а текстовый (а уж коли он выполняемый,
значит это текст программы на некотором алгоритмическом языке) то первая его
строка должна начинаться с символов # ! и содержать командную строку для
запуска евонного интерпретатора. А для самого этого интерпретатора — выглядеть
как комментарий. Вот и. А остальные такие символы — за компанию.
Пробелы, как и во всех других языках, между словами можно вставлять в
любых потребных количествах. В том числе и ни одного, если слова разделяются
какими-то другими символами-разделителями. Впрочем, в этом случае — если
границы слов Фокалу очевидны, то и запятые без надобности. (Что известно как
"синтаксический сахар".) Без запятых не обойтись только в случае, когда значим
и пропуск элемента. Ну и в выражениях правила более строгие...
* * *
Таким образом в памяти фокаловского интерпретатора (а значит и в
распоряжении пользователя) есть два вида ресурсов: программные строки и
переменные. Переменная хранит одно число, любое, а программная строка — тоже
любую строку, совсем не обязательно "программную": никто же не проверяет этого!
Только когда попытаются это выполнить...
Далее: программные строки различаются номерами, а это числа, с которыми
Фокал и работает. А вот переменные различаются по именам, а это строки (пусть
пока и всего из двух символов). Симметрия проглядывается? Вот только со
строками Фокал работать не умеет. Или (пользуясь тем, что строка — это
последовательность символов, а символ обозначается коротеньким целым числом)
таки умеет, но очень-очень-очень ограниченно... (С помощью спецфункции
ввода/вывода FCHR.)
Научить Фокал работать со строками было бы очень желательно: пригодилось бы
для множества практических целей... Вот только действия со строками совсем не
такие как с числами...
Первый подход к снаряду (а для Бейсика и единственный) — "полиморфизм".
(Оно же "динамическая типизация".) Вот те самые типы данных, от которых я выше
шарахнулся, как черт от ладана. Однако в Бейсике именно так и сделали,
сотворив из маленького и простого языка (хотя и малость аляповатого и не
больно то хорошо продуманного) сложного и громоздкого монстра...
Полиморфизм это когда одна и та же переменная может принимать значения
разных типов. И одни и те же операции применимы к ним ко всем. (Ну может быть
с некоторыми ограничениями?...)
В общем я тоже было решил хранить строки, с которыми собираемся работать,
в тех же самых переменных что и числа. Проигнорировав (или скорее просто не
сообразив), что строки-то у нас уже есть — программные. Впрочем, так делают
все и во всех алгоритмических языках... Ну вот и я туда же: придумал, как
доопределить пять фокаловских операций для работы со строками. С этого
собственно и начался проект модернизации языка "Фокал-3", числа в котором
должны были быть не простыми, а гиперкомплексными. Так до сих пор и не
реализованный. ("Три" потому что Фокал-2 уже был. И было там много чего,
включая "псевдонимы" — чтобы можно было давать подпрограммам "говорящие" имена,
подгружать код, не занимающий пространства номеров строк и доступный вот через
такое имя; переименовывать встроенные функции, сняв с имён переменных "табу"
на букву Ф, а так же передавать подпрограмме параметры "по ссылке" а не только
"по значению"... Вот только числа там были обычные — с плавающей запятой...)
Но перед этим, в качестве тренировки (да и кое-для каких практических
целей) решил сперва немножко развлечься — сделать слегка "доведенную до ума"
реализацию традиционного Фокала (назвав это Фокал-1А). Вот до сих пор и
развлекаюсь...
Идея там была вот какая: строка + строка это их сцепление ("конкатенация");
строка * число, равно как и в обратном порядке (потому как от перестановки
сомножителей произведение не меняется) — это размножение строки, дублирование
указанное числом количество раз. При чем число может быть и дробное. (И тогда
порядок операндов уже имеет значение...) А если отрицательное, то строка
инвертируется. Унарный минус — чисто инверсия строки (т.е. буковки в обратном
порядке), а унарный плюс — получение длины (т.е. результат — число). (А для
комплексного числа унарный плюс это получение комплексно-сопряженного.)
Соответстенно деление строки на число это её разрезание. Вернее — взять часть
строки с указанного символа, которые нумеруются с первого, чтобы на ноль тоже
было делить нельзя. В обратном порядке (т.е. число / строка) можно — результат
будет ноль или указанное числом количество символов. Если число отрицательное,
то с конца.
Вычитание строк это их сравнение: поиск позиции, с которой одна строка
входит в другую: числа в операторе If сравниваются именно так! Если вторая
строка "больше" — результат будет отрицательный, а если ни одна из них не
часть другой — то ноль.
Вычитание значения из самого себя это еще и возможность определить его тип:
для числа получится ноль, для строки — единица (она же сама в себя входит с
первой позиции!), а вот для "пустого" значения результат будет -1.
Это самое пустое значение при любых других операциях даёт ошибку, а содержится
в неинициализированной переменной, в том числе локальной, если был пропущен
параметр соответствующий её позиции.
Сложение и вычитание строки и числа я было хотел запретить — сделать чтобы
они давали ошибку, но если трактовать число как код символа и соответственно
числовое значение как односимвольную строку... Встроенная в Фокал функция FCHr
делает именно так, значит можно и тут.
Операция ^ и для чисел самая трудозатратная, так пусть такой же будет и
для строк: "полукорреляция" — сопоставление двух строк на предмет поиска общей
подстроки. Вернее самой длинной из них, буде совпадений окажется несколько.
А "полу" потому, что неполная. А то полная корреляция строки с собою найдёт её
всю целиком. (А то мы не знаем, что совпадает сама с собою!) Нам вообще-то надо
повторяющийся фрагмент: (S/2)^S (здесь мы в первом операнде отрезали от S
первый символ — теперь не совпадёт). А коли так уж нужна полная — извольте:
fmax(S1^S2,S2^S1).
Ну что можно сказать? Всё вроде бы логично, разве что, с одной стороны,
слишком много внимания не больно-то реально нужным реверсу строки и её
размножению (но это диктуют имеющиеся у нас операции), а с другой — всего этого
явно недостаточно: возможных действий со строками куда больше...
Например буковки бывают заглавные и строчные, а так же русские и латинские.
При сравнении двух строк (в том числе на полное равенство: (S1-S2)*(S2-S1)) в
одних случаях все их надо считать разными, а в других — совпадающими. Ну и как?
(Ввести некие внутренние признаки эквивалентности, устанавливать и сбрасывать
их некоторой спецфункцией? Некрасиво получается...) И это далеко не
единственное...
Вон в Бейсике ввели для работы со строками кучу функций, "поручив"
встроенным операциям только самое очевидное, типа конкатенации. Нам такого
щастья и даром не надь... Сложно, громоздко и необозримо даже с помощью
встроенной справочной системы.
В общем оставались у нас две "неокученные" операции: умножение и деление
строки на строку. Вот и решено было, что второй операнд в них — не простая
строка, а специальная — составленная по некоторым правилам. Строка1 / строка2
это будет сопоставление с шаблоном, а строка1 * строка3 — "перекодировка".
Здесь строка3 это последовательность ПАР шаблонов: включающих анализирующий
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |