перераб. Это моя N-я попытка объяснить это.
Предположим, у вас есть простая детерминированная процедура, которая выполняется многократно, всегда следуя одной и той же последовательности выполнения операторов или вызовов процедур.
Вызовы процедуры сами записывают все, что они хотят, в FIFO последовательно, и они читают то же количество байтов с другого конца FIFO, например: **
Вызываемые процедуры используют FIFO в качестве памяти, потому что они читают то же, что и при предыдущем выполнении.
Поэтому, если их аргументы в этот раз отличаются от прошлых раз, они могут это увидеть и сделать с этой информацией все, что захотят.
Чтобы начать, должно быть начальное выполнение, при котором происходит только запись, а не чтение.
Симметрично, должно быть окончательное выполнение, при котором происходит только чтение, а не запись.
Таким образом, существует регистр «глобального» режима, содержащий два бита: один для чтения и второй для записи, например:
Первоначальное выполнение выполняется в режиме 01 , поэтому выполняется только запись.
Вызовы процедур могут видеть режим, поэтому они знают, что предшествующей истории нет.
Если они хотят создавать объекты, они могут и помещают идентифицирующую информацию в FIFO (нет необходимости хранить в переменных).
Промежуточные исполнения выполняются в режиме 11 , поэтому происходит чтение и запись, а вызовы процедур могут обнаружить изменения данных.
Если есть объекты, которые нужно обновлять,
их идентификационная информация считывается и записывается в ФИФО,
чтобы к ним можно было получить доступ и при необходимости изменить их.
Окончательное выполнение выполняется в режиме 10 , поэтому происходит только чтение.
В этом режиме вызовы процедур знают, что они просто очищаются.
Если какие-либо объекты поддерживаются, их идентификаторы считываются из FIFO и могут быть удалены.
Но реальные процедуры не всегда следуют одной и той же последовательности.
Они содержат заявления IF (и другие способы варьирования того, что они делают).
Как это можно сделать?
Ответ - это особый вид оператора IF (и его завершающего оператора ENDIF).
Вот как это работает.
Он записывает логическое значение своего тестового выражения и читает значение, которое тестовое выражение имело в прошлый раз.
Таким образом, он может сказать, изменилось ли тестовое выражение, и принять меры.
Для этого необходимо временно изменить регистр режима .
В частности, x - это предыдущее значение тестового выражения, считанное из FIFO (если чтение разрешено, иначе 0), а y - текущее значение теста выражение, записанное в FIFO (если запись включена).
(На самом деле, если запись не включена, тестовое выражение даже не оценивается, и y равно 0.)
Тогда x, y просто маскирует регистр режима r, w .
Таким образом, если тестовое выражение изменило с True на False, тело выполняется в режиме только для чтения. И наоборот, если оно изменилось с False на True, тело выполняется в режиме только для записи.
Если результат равен 00 , код внутри оператора IF..ENDIF пропускается.
(Возможно, вы захотите немного подумать о том, охватывает ли это все случаи.)
Это может быть неочевидно, но эти операторы IF..ENDIF могут быть произвольно вложены, и их можно распространить на все другие виды условных операторов, таких как ELSE, SWITCH, WHILE, FOR и даже на вызов функций, основанных на указателе. Это также тот случай, когда процедуру можно разделить на подпроцедуры в любой желаемой степени, в том числе рекурсивной, при условии соблюдения режима.
(Существует правило, которое должно соблюдаться, называемое правило стирания , то есть в режиме 10 нет вычислений каких-либо последствий, таких как следование указателю или Индексирование массива должно быть сделано. Концептуально, причина в том, что режим 10 существует только с целью избавиться от вещей.)
Таким образом, это интересная структура управления, которую можно использовать для обнаружения изменений, обычно изменений данных, и принятия мер в отношении этих изменений.
Его использование в графических пользовательских интерфейсах позволяет поддерживать некоторый набор элементов управления или других объектов в соответствии с информацией о состоянии программы. Для этого использования три режима называются SHOW (01), UPDATE (11) и ERASE (10).
Изначально процедура выполняется в режиме SHOW, в котором создаются элементы управления и соответствующая им информация заполняет FIFO.
Затем выполняется любое количество выполнений в режиме ОБНОВЛЕНИЕ, где элементы управления изменяются по мере необходимости, чтобы оставаться в курсе состояния программы.
Наконец, в режиме ERASE выполняется выполнение, при котором элементы управления удаляются из пользовательского интерфейса, а FIFO очищается.
Преимущество заключается в том, что после написания процедуры создания всех элементов управления в зависимости от состояния программы вам не нужно писать что-либо еще, чтобы сохранить ее. обновлен или убран позже.
Все, что вам не нужно писать, означает меньше возможностей совершать ошибки.
(Существует простой способ обработки событий пользовательского ввода без необходимости писать обработчики событий и создавать для них имена. Это объясняется в одном из видео, ссылки на которые приведены ниже.)
С точки зрения управления памятью вам не нужно составлять имена переменных или структуру данных для хранения элементов управления. Он использует только достаточно памяти для видимых в данный момент элементов управления, в то время как потенциально видимых элементов управления может быть неограниченным. Кроме того, никогда не возникает проблем со сборкой мусора ранее использованных элементов управления - FIFO действует как автоматический сборщик мусора.
С точки зрения производительности, когда он создает, удаляет или изменяет элементы управления, он в любом случае тратит время.
Когда он просто обновляет элементы управления, а изменений нет, циклы, необходимые для чтения, записи и сравнения, являются микроскопическими по сравнению с изменением элементов управления.
Другое соображение производительности и правильности, относительно систем, которые обновляют отображение в ответ на события, состоит в том, что такая система требует, чтобы на каждое событие отвечали, а не дважды, в противном случае отображение будет неправильным, даже если некоторые последовательности событий могут быть самоуничтожающимся. При дифференциальном выполнении проходы обновления могут выполняться так часто или редко, как требуется, и в конце прохода отображение всегда корректно.
Вот чрезвычайно сокращенный пример, где есть 4 кнопки, из которых кнопки 2 и 3 являются условными для логической переменной.
- На первом проходе в режиме Show логическое значение false, поэтому отображаются только кнопки 1 и 4.
- Затем для логического значения устанавливается значение true, и проход 2 выполняется в режиме обновления, в котором создаются кнопки 2 и 3, а кнопка 4 перемещается, давая тот же результат, как если бы логическое значение было истинным при первом проходе.
- Затем для логического значения устанавливается значение false, и этап 3 выполняется в режиме обновления, в результате чего кнопки 2 и 3 удаляются, а кнопка 4 возвращается на прежнее место.
- Наконец, проход 4 выполняется в режиме стирания, в результате чего все исчезает.
(В этом примере изменения отменяются в обратном порядке, как они были сделаны, но в этом нет необходимости. Изменения можно вносить и отменять в любом порядке.)
Обратите внимание, что в любое время FIFO, состоящий из соединенных вместе Старого и Нового, содержит в точности параметры видимых кнопок плюс логическое значение.
Смысл этого в том, чтобы показать, как можно использовать одну процедуру «рисования» без изменений для произвольного автоматического инкрементного обновления и удаления.
Я надеюсь, что ясно, что это работает для произвольной глубины вызовов подпрограмм и произвольного вложения условий, включая циклы switch
, while
и for
, вызов функций на основе указателей и т. Д.
Если мне придется это объяснить, тогда я открыт для того, чтобы сделать объяснение слишком сложным.
Наконец, есть пара сырых, но коротких видео, размещенных здесь .
** Технически, они должны прочитать то же количество байтов, которое они написали в прошлый раз. Так, например, они могли написать строку, которой предшествует количество символов, и это нормально.
ДОБАВЛЕНО: Мне потребовалось много времени, чтобы убедиться, что это всегда будет работать.
Я наконец доказал это.
Он основан на свойстве Sync , что примерно означает, что в любой точке программы число байтов, записанных на предыдущем проходе, равно числу, считанному на последующем проходе.
Идея доказательства заключается в том, чтобы сделать это индукцией по длине программы.
Самым сложным для доказательства является случай раздела программы, состоящий из s1 , за которым следует IF (тест) s2 ENDIF , где s1 и s2 - это подразделы программы, каждый из которых удовлетворяет свойству Sync .
Делать это только в тексте - это глазу на глаз, но здесь я попытался изобразить это:
Он определяет свойство Sync и показывает количество байтов, записанных и прочитанных в каждой точке кода, и показывает, что они равны.
Ключевые моменты заключаются в том, что 1) значение тестового выражения (0 или 1), считанное на текущем проходе, должно равняться значению, записанному на предыдущем проходе, и 2) условие Sync (s2) имеет вид довольный.
Это удовлетворяет свойству Sync для объединенной программы.