| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
элементов массива — с нуля.)
1.1 T "вводите числа, 0 — последнее"!; S N=-1;
1.2 A y; D 2; If(y) 1.2, 1.3, 1.2; C помещение в массив вынесли в группу 2
1.3 If(N) 1.4,1.4; X FSBr(3,0); T !"Всё."!; Q
1.4 t "Чего же сразу-то ноль? Вот даже выдавать нечего..."!; Q
C не только x[]=y но и ссылки на поддеревья устанавливаем...
2.1 S N=N+1; S x[N]=y; S m[N]=-1; S b[N]=-1; S i=0; If(-N)2.2; R;
2.2 S z=x[i]-y; S j=b[i]; I(z) 2.3; S j=m[i]
C то же самое на Си: j = (y>=x[i]) ? b[i] : m[i]; по-моему куда понятней!
2.3 If(j) 2.4; S i=j; G 2.2; C спуск по дереву линейный — можно без рекурсии
2.4 If(z) 2.5; S m[i]=N; R; C очередное число повесим как "листик"
2.5 S b[i]=N; R;
C Вот он — обход дерева с помощью рекурсии. Почти в одну строчку...
3.1 If(&) 3.2; X FSBr(3,m[&]); T x[&]; X FSBr(3,b[&]);
3.2 R
Надо ли тут чего разжевывать, или и так всё понятно? Ну разве что — что
квадратные скобки при обращении к массиву — выпендрёж чистой воды: это не Си,
можно любые... Впрочем, и построение бинарного дерева вместо сортировки вот для
такой задачки — тоже выпендрёж. А вот его обход (уж коли у нас дерево) — нет:
он возможен только рекурсией. (Но штука таки дорогая — стэк жрет неумеренно...)
Но обычно в качестве примера на рекурсию все и всегда приводят вычисление
факториала: произведения всех чисел от 1 до N (что обычно обозначается как N!)
потому что видите-ли: (N+1)! = (N+1)*N! Они что — опухли?! Здесь дерево
вырождается в линейную последовательность, для обхода которой достаточно
обычного цикла. Однако вместо того, чтобы написать:
s X=1; f i=2,N,1; s X=X*i; C и всё: X==N! — осталось только напечатать
t N,"! = ",X
и поискать другой, более содержательный пример, сооружают нечто типа:
4.1 if(1-&)4.2; x 1; r
4.2 x &*fsbr(4,&-1); r
t N,"! = ",fsbr(4,N)
Как ни странно, это будет работать, даже если переменная & не локальная.
А вот если бы написали не &*fsbr(...) а fsbr(...)*& — вот тогда нет! Понятно,
почему?
Разумеется, у меня локальные переменные тоже создаются автоматически в
момент первого присваивания. Но уничтожаются при выходе из подпрограммы. При
этом становится доступным предыдущий комплект, если он был. Кроме того, если
данная локальная переменная не существует — это не ошибка, как для глобальной:
она ищется в предыдущих комплектах, а если так и не найдена — то будет ноль.
* * *
И вот теперь, когда с "каноном" — "базовым", исходным Фокалом, надеюсь,
всё более-менее понятно, краткое содержание первой части сего опуса.
(Напоминаю: опус это сам Фокал-1А.)
Всё вышеописанное я либо просто воспроизвёл (сохранив и то, что обычно
считают недостатками: всего два значащих символа в имени переменной и "вето"
на букву Ф), либо немножко модернизировал. Прежде всего уточнил и дополнил
концепцию ввода/вывода, в том числе в части взаимодействия с файловой
системой ОС. Ну и так, кое-что по-мелочи — разные удобства и красивости:
— оператор Help для получения справок; просмотрщик к нему (аналог
UNIX`овского more) чтобы их с удобством просматривать; "экранный" редактор
командной строки со всяческими прибамбасами, чтобы с удобствами её набирать.
Или править с помощью оператора Modify. Работающего поэтому только и
исключительно с терминалом, независимо от того куда направлены каналы ввода и
вывода.
— вот эти самые по-настоящему локальные переменные — как позиционные
параметры при вызове подпрограммы с помощью FSUbr, так и сами по себе. И плюс
к ним — произвольное количество параметров не только у FSUbr но и у других
встроенных функций (де лишние будут просто игнорироваться). А так же
возможность пропуска параметра или параметр в виде текстовой константы — для
расширения функциональности без увеличения количества этих самых встроенных
функций. Например FSQrt(X) — просто квадратный корень, а FSQrt(X,N) — корень
N-ой степени; FLOg(X) — логарифм натуральный, а FLOg(X,N) — по основанию N.
Или суровая и опасная функция FX( действие, адрес, параметр ) производящая
действия с управляющими регистрами внешних устройств (каковых действий всего
три: чтение, запись и побитовое-И — например на предмет проверки признака
готовности), без первого аргумента: FX( , А, Б) не лезет ни в какие
регистры или аппаратные порты ввода/вывода, а всего лишь выполняет побитовое-И
между А и Б, рассматривая их как длинные целые: бывает, что надо и в других
местах выделять отдельные биты. (Или, используя не вполне корректные
фокусы-покусы со знаками А и Б — другие побитовые операции...) Или например
так же самая FSBr() без аргументов, не вызывая никаких подпрограмм, сразу
возвращает содержимое аккумулятора. (Куда иногда могут "зелететь" и такие
вещи, как например количество строк, реально выданных на экран просмотрщиком,
которые не узнать больше никак...)
((Вот тут я должен покаяться: я же обещал, когда всё это затеял, не вводить
в Фокал ничего нового — использовать только то, что там было изначально.
Добавив ну может быть парочку функций и операторов. То есть расширять чисто
количественно. А я таки ввёл: концепции игнорирования выражения, пропуска его
без вычисления в традиционном Фокале небыло. Всё остальное там было, а вот
этого — нет. А это — отдельный механизм квазивычислений. Но деваться было
некуда: условному оператору If (см. далее) никак нельзя давать вычислять все
указанные в нём выражения — только одно. Из за их возможного побочного эффекта.
Но уж коли лошади уже убежали — нет смысла запирать конюшню: если что-то где-то
таки используется, значит это можно (и нужно) использовать везде! Это я про
игнорирование лишних, не предусмотренных конструкцией языка, аргументов функции.
А вот всё остальное там было...))
— парочка-троечка функций с побочными эффектами: FColoro (она же FЦвет) для
указания цвета выводимых на экран символов (включая коррекцию палитры);
FKурсор (она же FCS) для перемещения по экрану курсора (включая считывание
уже выведенных на экран буковок, изменение их цвета, а так же рисование на нём
линий и рамочек символами псевдографики); FBIp для издания звуков с помощью
встроенного в компьютер динамика (включая ноты и азбуку морзе); FMuso (она же
FMышь) для работы с мышкой; FMIn и FMAx (без побочных эффектов) призванные
(вместе со сменой знака в роли инверсии) служить логическими операциями И и
ИЛИ — в том числе для троичной или нечеткой логики. FTMp для получения текущей
даты/времени (в виде количества суток от начала тысячелетия, где время это
дробная часть суток) а так же для других действий с датой/временем, включая
преобразование в удобочитаемый вид и обратно.
А "удобочитаемый вид" это вообще-то текстовая строка! И единственное, что с
нею можно было сделать это отправить в канал вывода. Но потом, в части второй,
обнаружилась и стала интенсивно использоваться переменная под такую вот
текстовую строку, она же — аккумулятор А2. И тогда стало естественным поместить
вот такой текстовый побочный эффект туда. Но так уж исторически сложилось...
В общем этот исторически сложившийся бардак, когда у одних функций с этим
так, а у других эдак, продолжался буквально до вчерашнего дня. Когда наконец
все функции с текстовым побочным эффектом были приведены к общему знаменателю.
(Всётаки вот такие вот "акты ревизии" как вот этот, пишутся совсем не зря.
Таки позволяют выявлять имеющие место несообразности и/или технологические
потребности...) В общем я сделал по аналогии с функцией FTEll (она же теперь и
FOper) где этот самый текстовый побочный эффект включается наличием второго
аргумента. Которого может и не быть — достаточно отделяющей его запятой:
FTEll("имя_файла", ); Соответственно, переключение его на А2 — так же:
FTEll("имя_файла",, ); и для других подобных функций — аналогично: FTMp(t,)
FKurs("k",) FTEll(,,N_арг,)
Но вот если они вызваны внутри форматного выражения оператора Write %формат
(о котором пока что речи еще небыло) как раз и работающего с А2, то помещение
туда текстовой строки — подавляется. Однако она имеет шанс стать операндом — в
случае если в этом месте предполагается именно строка, а не число:
{"..."__FTMp(t,)...} или #{FTMp(t,)} в последнем случае конструкция %...
если есть — ингорируется. Впрочем, это мы безбожно забежали вперёд...
— оператор Vizual и парная к нему функция FViz на предмет "управления
визуализацией". А на самом деле получения параметров экрана (например высоты и
ширины), а так же режима работы дисплея. Включая их переключение, в том числе в
один из графических режимов и отрисовку там графичесских примитивов.
Однако, эти примитивы оказались слишком примитивными (и пока что этот
оператор отправлен на реконструкцию, а функции сией функции — урезаны): Фокал
таки калькулятор, и графика ему нужна чтобы можно было рисовать графики -
компактно и наглядно отображать результаты вычислений. За сим графическими
примитивами должны быть вовсе не "точка", "линия", "дуга окружности",
"полигон" и закраска оного, а оси координат (вместе с началом координат,
масштабом по каждой оси, в том числе, возможно, нелинейным, и делениями на ней)
и уже только после этого точки графика (возможно объединённые в "коллекции"),
положения которых на экране автоматически пересчитываютя в соотвтествии с
масштабом осей. Эта часть графики — "векторная". А "растровую" предполагается
поручить двум функциям FТочка и FЛиния, как это было в Фокале на БК-0010,
организованным по аналогии с FKурсор...
— некоторая модернизация некоторых операторов:
* Вычисление возвращаемого значения прямо в операторе Ret, делающее его
аналогом Xecut. (Чисто на будущее: чтобы если и когда будут параллельные
процессы, запускаемые оператором Job — чтобы между вычислением возвращаемого
значения и возвратом из функции не вклинилось что ни будь, что испортит нам
всю малину.)
* Передача оператором Do параметров в подпрограмму по аналогии с FSBr
* Дополнительные ключевые слова в операторе Write чтобы сообщал дополнительные
сведения (в форме пригодной для последующего ввода), в том числе о результатах
деятельности операторов Set Oper Do Break... Аналогично и для Eraze — буде
там найдётся что очистить.
* Несколько выражений в операторе Xecut и несколько присваиваний в операторе
Set — выпендрёж чистой воды. (Ибо экономия получается — копеечная.) А вот
избавляться от ключевого слова Set (как первое что сделали в Бейсике — сделали
чтобы можно было не писать LET) — это фиг: нарушение принципов! Да и экономия
в два символа, включая пробел...
* Меры по повышению "скриптовости" операторов передачи управления If и Go.
Это когда программа не как обычно — сперва грузится в память и только потом
запускается на выполнение, а сразу выполняется по мере чтения из файла. Который
называют "командным" (а нынче — "скриптовым"). Особенность тут в том, что
все строчки командного файла — "прямые". А операторам передачи управления
подавай номер строки!. Чтобы от них здесь был хоть какой-то толк, надо бы номера
строк как ни будь того... Равняясь на оператор For, у которого тело цикла это
остаток строки, вспомним, что и у If есть то же самое — когда последние адреса
перехода отсутствуют. Ну так разрешим, чтобы любые, а не только последние -
при условии сохранения разделяющих их запятых. И плюс к этому пусть нулевой и
отрицательный номер строки тоже что-то значат: нулевой — ничего не делать
(перейти к следующей строке), а отрицательный — к следующему оператору, то есть
к остатку строки, как и пропуск. (А то вызовем FSBr, а она...) И тогда можно
например так:
If (X) 0, ,0; Type " икс равен нулю "!
If (X) ,0 ; Type " икс не равен нулю"!
Дополнительно: из трёх выражений в операторе If теперь вычисляется только одно,
а остальные — пропускаются. Потому что эти выражения вполне могут давать
побочный эффект. И/или в выборе одного из них — смысл всего этого оператора...
По аналогии с If — оператор Go 0; передаёт управление на начало текущей
строки и таким образом позволяет сделать цикл в одной строчке. Например:
Set c=FCHr(-1); type c; If(c-27),0; Go 0; Coment 27 — код нопки ESC
Эта програмка в одну строку будет выдавать коды нажатых кнопок, пока
пользователь не нажмёт кнопку ESC, она же "эскейп".
* Модернизация оператора For на предмет борьбы с плавающей арифметикой:
сделано так, что цикл завершается, когда параметр цикла соберётся выскочить за
конечное значение более чем на пол шага. (Но после завершения цикла остаётся
таким, каким был при последней итерации.) Ну и за одно — автоматический выбор
шага, если он опущен: еще и -1 если конечное значение меньше начального. А
опустить можно не только шаг, или даже конечное значение (получится аналог
оператора присваивания), но и вообще всё. Остаток строки выполнится ровно один
раз, но как подпрограмма — что нам тоже понадобится... (Чуть ниже.)
— Реакция на "события" и "ситуации" (в т.ч. ошибки). Она устанавливается:
Break С = реакция; "Реакция" здесь это номер строки или группы, которую
надо запустить, когда произойдёт оное событие или случится ошибка/ситуация.
Хотя это может быть и остаток строки: Break С = ; остаток строки...
Если С < 0 то это ВНЕШНЕЕ "событие". И ловушка на него — глобальная.
(Удалить — только явно, указав нулевую реакцию.) Для ДОС`а первые 256 из них
это реальные прерывания (а под UNIX`ом это будут сигналы); с номерами больше
1000 — свои собственные, типа окончания звукового фрагмента, временного
интервала, заказанного функцией FCLk, и.т.п. К сожалению количество таких
ловушек очень ограничено — средство это таки экспериментальное. Интерпретатор
Фокала — медленный; реакция на событие "вклинивается" между выполнением
операторов и выглядит так, как будто между ними вставили оператор Do, но
когда это еще будет... Поэтому внешние события только "фиксируются" -
наращивается значение связанного с ловушкой счетчика. При запуске реакции оно
передаётся первым аргументом, а счетчик — сбрасывается. Принудительно
обнулить все эти счетчики: Eraze Break;
Если С >= 0 то это ВНУТРЕННЯЯ "ситуация", в том числе ошибка. Ловушка
на неё — локальная. (Автоматически удаляется при возврате из поставившей её
подпрограмы вместе с локальными переменными.) По аналогии с программными
строками, события объединяются в группы (ошибки: группы событий 0 — 5), ловушку
можно поставить как на всю группу, так и индивидуально. Возникнув, ситуация
| Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |