Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
в промежуточную — строчных, а из неё обратно — заглавных... Ну еще константы
конечно. Не только десятичные (битики указывать, они не больно удобны), а как
в Си: 0х... 16-ричная, 0о... 8-ричная и 0b... двоичная. А еще текстовые
константы в виде кода буковки. Эти не 'в кавычках', а с помощью кавычки: 'А.
Дело то уже в кавычках происходит! Да и буковка нам как правило нужна только
одна. Хотя конечно с ними можно как с точками...
Ага, а что делать, если попадётся вовсе не буковка, а цифра, знак
препинания или упаси господи вообще какой ни будь неизвестный науке зверь,
которых дофига в уникоде? Их же отличать как-то надо! Да, конечно они
отличаются — битами признаков в старшей части... Но надо же не просто отличить,
но и поступить по-разному... Поэтому без условных операций ну никак не
обойтись!
Их две: ? и для случаев "да" и "нет", пропускающих в противном
случае следующую вслед за ними операцию. Которая может быть и составной, для
чего (разумеется!) используются круглые скобки. Сами-то по себе они ничего не
делают (от слова "вообще"!) — исключительно указывают вот такой вот условной
операции что именно пропускать. Без открывающей скобки она просто "не
догадается", а без закрывающей — пропустит вообще всё. (Но и вложенность
скобок тоже отслеживает, поэтому лучше использовать их парами. Хотя ничто не
мешает...) А перед условной операцией должен быть типа "предикат"?...
Никто никому ничего не должен! (Если не занимал, конечно...) Просто каждая
операция вырабатывает еще и "логический" результат. И почти всегда он
"нейтральный" — /0/. (Фокал ненавязчиво приучает нас к тренарной логике!) Даже
бездельник _ (подчеркивание) и то вырабатывает нейтральный результат. И одни
только круглые скобки оставляют его без изменения. Но не для того, чтобы
городить аналог if-else: ?(...)(...) этот фокус и так получится, а чтобы
последняя операция заключенной в скобках последовательности при случаем могла
что-то проверить для следующей после них условной операции...
...как вот эти самые "предикаты": < <= > >= = и <> (не равно) а так же ><
(включение), которые не-нейтральный результат и вырабатывают: /+/ — если
условие выполняется и /-/ если нет. Они, ничего не изменяя, сравнивают два
верхних операнда. А если перед ? такого предиката нет, то (при /0/) он сам
проверяет, что верхний операнд на стэке не-ноль.
Обратим внимание, что если сразу вслед за знаком '=' у нас буква или цифра,
то это присваивание, а если что угодно другое — то предикат: сравнение на
равенство.
Логические константы? Да они у нас тоже есть: M и S ("Младший" и "Старший"
потому что... а вот не будем забегать вперёд!) дающие "негативный" (/-/) и
"позитивный" (/+/) логический результат соответственно. А "нейтральный" (/0/)
нам даст любой бездельник.
Логические операции? Ишь чего захотели: они же бинарные! Поэтому — где ни
будь в другом месте. К тому же значений — три, а у нас здесь все числа
исключительно беззнаковые. Хотя можно (операцией 'F') сохранить логический
результат в числовом аккумуляторе А1 (мы про него еще не забыли?), и сразу же,
пока никто не испортил (А1 — что твой проходной двор!) положить на стэк его
содержимое (операцией 'а'). Но негативный результат превратится в число -1 и
будет выглядеть как единицы во всех разрядах. Однако, если нас такое устроит...
Итак: 'a' 'A' (любого алфавита) — прочитать на стэк число из аккумулятора
А1 и соответственно его туда записать. А 'f' и 'F' — сохранить в А1 логический
признак результата предыдущей операции и установить обратно.
Общий принцип такой: заглавная буква — наружу, на экспорт, строчная -
внутрь. Аккумулятор А1 снаружи данного механизма? Да! Значит пишет туда 'F', а
берёт оттуда и устанавливает соответственно 'f'.
Но ведь операция инверсии — унарная? Да! И таковых у нас аж целых три:
p n c (все буквы маленькие латинские) — "позитив", "негатив" и "циклатив".
— позитив 'p' оптимистично превращает нейтральный результат в позитивный.
— негатив 'n' переворачивает всё кверх ногами (оставляя нейтральный как был)
— циклатив 'c' это здешний пессимист, желающий видеть всё хуже, чем оно есть,
и для этого последовательно понижающий оценки: позитивную до нейтральной,
нейтральную до негативной... А вот негативной деваться некуда — занимает
оставшуюся свободной позитивную позицию.
Зачем такие сложности? А вот:
— Никогда ничего сама не проверяющая операция Х выполнит операцию Х только
если /-/, nХ — только если /+/, и соответственно cХ — только если /0/.
— Операция ? проверяющая в "нейтральном" случае на ноль — чтобы ничего не
проверяла, должна быть употреблена как p? — сработает если /+/ и /0/;
np? — если /-/ и /0/. И наконец cp? — если НЕ-/0/.
— А следующая за ней операция соответственно сделает всё наоборот.
Вот такая она — троичная логика в проекции на двоичную.
Логические константы 'M' и 'S' это еще и префиксы-модификаторы для
операций обращения к регистрам #N и =N — чтобы они делали это по "вагонному"
принципу. (Именно префиксы: сам то логический результат выполнения предыдущей
операции ни на кого, кроме ? и никакого влияния не оказывает.)
Так-то #N всего лишь берет из регистра "текущий" i-ый байт, а =N пишет
поверх него. (Если он есть. А буде нету — размер строки увеличивается и
вырабатывается /+/. А буде нету чего прочитать — читается ноль а логический
результат — /-/.)
Как уже говорилось, смещение текущего байта и количество оставшихся можно
получить операциями 'i' и 'l', а установить? ('I' и 'L') Увы — нет. То есть
если бы мы байтиками и ограничились, то никаких проблем. Но мы же разрешили
(сами себе) таскать по нескольку байт, при чем в обе стороны. В результате
элементарно запутаться... Да и зациклиться. Поэтому всё строго последовательно.
(Нет, мне не жалко, я ведь сделаю... Но пеняйте на себя!)
А вот по "вагонному" принципу байт добавляется в строку самым младшим и
самым старшим соответственно. В точности как вагончики к стоящему на рельсах
составу (пока он еще без локомотива). И забираются в точности так же. (А буде
забирать нечего — то ноль и негативный логический результат.)
Старший братец %R делает всё в точности то же самое, но пишет не один
байт, а всё значение, которое у него в верхней ячейке стэка. (А оно может быть
какого угодно размера — от нуля байтов и больше — неограниченно, хотя и в
пределах разумного.) А вот читает и удаляет при обращении по вагонному
принципу "номинальное" количество байтов, которое устанавливается командой 'N'.
А вот считать, что ранее установлено — командой 'i'. (Потому как 'n' занято,
а индекс текущего байта обрабатываемого фрагмента для %R смысла не имеет: он
берёт его весь целиком.) Соответственно 'l' и 'L' — определяет вот этот самый
размер операнда (в байтах) и принудительно его устанавливает — либо урезая
строку, либо напротив добавляя ей ведущие нули.
(Впрочем, "ведущие" это старшие биты — в конце строки, а отнюдь не в её
начале, где у нас биты младшие. А вот когда строка как текст выдаётся на
печать, то слева оказываются как раз младшие, самые первые буквы.)
А размер только значащей части (без ведущих нулей) сообщает операция 'л'.
Еще модификаторы 'M' и 'S' действуют на предикат >< (включение), так то
(без них) определяющий что один из операндов является частью другого. Для %r
где все операнды одинаковые, это просто сравнение на равенство. А вот для %R...
Кстати, операция побитовая: количество битов, на которое надо сместить
первый операнд относительно второго, чтобы они совпали, помещается в
аккумулятор А1. (Если второй операнд меньше первого и сдвигать пришлось
его — число отрицательное.) Причем, даже если совпадения нет, в А1
смещение для самого крупного из совпадающих фрагментов.
А вот с модификаторами 'M' и 'S' операция >< ничего и никуда не двигая,
определяет, сколько бит совпадают с младшего и старшего конца операндов.
Результат всё в так же в А1.
"Номинальный" размер операнда для %R нужен еще и при сдвигах. О которых
мы совсем забыли! Как же это побитовые операции и без сдвигов?...
Сдвигов у нас два вида: обычные << >> на указанное первым операндом количество
битов, и "циклические" N (где N — одна цифра) — на фиксированное. При
обычном сдвиге выдвинутые за пределы разрядной сетки биты теряются, а при
циклическом — задвигаются с другого конца. Для %r где размер операнда
фиксированный, всё очевидно. Для %R при сдвиге к старшим разрядам ничего не
теряется — увеличивается размер операнда. Теряется при сдвиге к младшим.
И при циклическом сдвиге >N если размер операнда меньше номинального, он
принудительно делается равным ему.
Сдвиги <0 и >0 которые ничего никуда не сдвигают, работают как своего рода
предикаты. И хотя вырабатывают неизменно нейтральный логический результат, но
считают количество нулевых битов с младшего и со старшего края операнда и
помещают результат в А1.
К сдвигам примыкает операция 'm' создающая "маску" в виде указанного
аргументом количества единиц. С её помощью можно выделить часть значения.
Например 6m& — только код буквы, обнулив все её признаки. Или 4m<7& выделить
только код типа алфавита — из соображения что он занимает 4 бита.
А главному баламуту всея Фокала символу % на пару с $ поручены операции
деления и умножения арифметики полиномов в кольцах Галуа. Где роль сложения
(а за одно и вычитания) выполняет ^ (исключающее ИЛИ).
Столь сложное и интрирующее название на самом деле скрывает за собою вещь
простую до тривиальности. Только малоизвестную: в школе этого не проходят. За
ненадобностью: лентяйская арифметика — без переносов между разрядами. (Поэтому
и сложение с вычитанием — там одна и та же операция.) И потому ни для чего
по большому счету не нужная — так, игра ума. Однако, почти как настоящая...
Вот и приспособили её для подсчета контрольных сумм для некоторой хранимой или
передаваемой по линиям связи информации — чтобы проверять: а не испортилась ли?
Небезызвестная "циклическая контрольная сумма" (она же CRC) ни что иное, как
остаток от деления информационного пакета, рассматриваемого как одно длиннющее
целое, на некую константу, называемую обычно "образующим полиномом". На эту
роль выбирается тамошнее простое число: в лентяйской арифметике тоже
встречаются числа, которые нацело ни на что не делятся. Кроме единицы и самоё
себя.
Деление обычной арифметики с переносами еще лучше бы подошло. Но это
считать куда проще. Особенно аппаратно: предположим информация приходит из
линии связи (то есть последовательно) и уже разбита на отдельные биты. Берем
сдвиговый регистр размером с вот этот вот "образующий полином" и
последовательно загоняем в него эти приходящие из линии битики. Но не просто
так, а берём те разряды сдвигового регистра, для которых в образующем полиноме
единички, и определяем для них не-четность. (С помощью кучи элементов
исключающее ИЛИ.) И если количество единичных битов там таки нечетное, то
входной бит инвертируем. (Еще одно исключающее ИЛИ.) Вот и всё! В начале в
сдвиговом регистре нули, в конце, после прохождения всего пакета — вот эта вот
циклическая контрольная сумма.
Еще есть операция 'h' подсчитывающая код Хеминга а за одно и определяющая
четность операнда (т.е. четное или нечетное там количество единичных битов).
Код Хеминга это контрольная сумма, позволяющая исправить одну ошибку (и
выявить — две). Он весь построен на идее четности: берем и делим биты операнда
на группы так, чтобы каждая из них включала примерно их половину. И
подсчитываем для каждой из них вот эту самую четность. Если испортился
(перевернулся) ровно один бит, то он испортит четность только тех групп, в
котороые входит. Разницу (результат исключающего ИЛИ) между правильной
контрольной суммой и новой, подсчитанной по вот такому испорченному операнду
обычно называют "синдромом ошибки". Чтобы этот самый синдром ошибки оказался
двоичным номером испорченного бита, группы выбираем так: пронумеруем все биты
операнда, поставив каждому в соответствие двоичное число. Групп сделаем ровно
столько, сколько разрядов в номере самого старшего бита. (Плюс еще одну,
включающую все биты операнда.) И каждой поставим в соответствие один из
разрядов номера. Бит операнда будет входить в те группы, для которых в его
номере в соответствующих им разрядах — единички. (Вот эти группы, а вернее
подсчитанные для них контрольные биты он, если что, и испортит!)
А теперь небольшой финт ушами: поручим хранить эти контрольные разряды тем
битам, в номере которых только одна единичка!
Вообще-то обычно контрольная сумма (и код Хеминга тоже) хранится либо совсем
отдельно, либо в конце. А надо бы — вот так, распределённо. Ну так эти биты
(уж коли они в конце или отдельно) из общего счета битов надо бы исключить.
Или туда биты контрольной суммы и прятать. Но пока не сделано ни то ни другое.
(Так что толку от операции h пока что не предвидится.)
Операция 'r' и 'R' — генератор случайных чисел. (Тот же самый, что и FRNd.)
Отлиличаются тем, что 'r' выдаёт случайное число размером в 1 байт, а 'R' — во
весь операнд (для %R — "номинального" размера).
Осталось управление порядком действий.
— условные операции ? и мы уже рассмотрели
— скобки ( ) для образования составной операции — тоже
— цикл в виде пары { } фигурных скобок. Открывающая просто указывает точку,
куда перейти, встретив закрывающую. Помещает свой адрес во внутреннюю
переменную. Поэтому '}' переход только назад. Если '{' небыло — к началу
командной последовательности. Вложенные циклы сделать нельзя — переменная
только одна. Но напоминаю, что инструмент — вспомогательный: сложные
программы здесь писать не предполагается.
— средств обращения к подпрограмме — тоже нет. Равно как и возможности
вычислить фокаловское выражение. В котором можно было бы вызвать спецфункцию
FSUbr и таким образом задействовать любые операторы Фокала. Не могу
представить, для чего бы это могло понадобиться применительно к каждой буковке,
обрабатываемого %r фрагмента. Хотя сделать впринципе можно: в виде конструкции
#{...}
— остановить выполнение — операцией '!'. Но для %r выполняющей одну и ту же
последовательность операций для каждого байта обрабатываемого фрагмента, это
всего лишь переход к следующему.
— совсем прекратить выполнение — по ошибке: злонамеренно выполнив деление на
Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |