Предыдущая серия была тут - #1163. :, PROCEDURE, FUNCTION. Параметры справа и слева.Часть 1.
Там мы рассмотрели ключевые слова :, ;, FUNCTION, PROCEDURE.
А также "параметры слева".
Рассмотрим теперь "параметры справа".
Пусть у нас есть пример с "параметрами слева":
Тут мы имеет "типичную" ОПЗ.
А что делать, если мы хотим пользоваться инфиксной нотацией?
Вот тут нам помогут параметры справа.
Перепишем наш пример с использованием параметров справа:
Подчеркну, что параметры справа передаются по ссылке.
Также можно написать:
Скобки пока обязательны.
Как обойтись без скобок - напишу отдельно.
Также наш пример можно переписать так:
Тут используется |^ вместо DO.
Они вообще говоря равноценны.
Про отличия я напишу несколько позже.
Метод |^ в аксиоматике определяется так:
Детали реализации |^ я также опишу позже.
Но пока отмечу, что |^ использует DO. Т.е. |^ является производным от DO.
Пойдём далее.
Зачем параметры справа передаются по ссылке,а не по значению?
Тому есть много причин.
В частности - "ленивые вычисления".
Рассмотрим реализацию булевских операций AND и OR.
Вот она:
Т.е. если по параметру aFirst - результат выражения будет ещё неясен.
Слово operator является аналогом слов : и FUNCTION. ОН лишь подчёркивает "операторную сущность" определяемых слов.
В частности - операторам можно задавать "приоритет выполнения" как например в Prolog.
Чтобы например избавиться от скобок в примере со словом Plus выше.
Но об этом расскажу отдельно.
Но пока будем считать, что operator определён как:
И что мы получаем с ленивыми вычислениями?
Если написать без ленивых вычислений:
То получим Access Violation.
А с ленивыми вычислениями:
Access Violation - не будет.
Надеюсь - понятно почему.
Операция <> кстати тоже определена в базовой аксиоматике при помощи правых и левых параметров. И через операцию =.
Вот так:
Комментировать не буду. Отмечу лишь, что операция ! - это постфиксное отрицание.
Пойдём далее.
Тот факт, что передаётся ссылка на слово, а не значение означает то, что если в качестве слова падали переменную, то мы можем писать в неё.
Реализуем например методы инкремента и декремента.
Так как они описаны в аксиоматике:
Понятное дело, что если мы напишем Inc 1, то мы получим ошибку если не компиляции, то времени исполнения.
Ну и предположим нам надо описать методы IncBy и DecBy.
Вот они:
И вызов:
Пойдём далее.
Параметры справа также удобно использовать для обращения к лямбда-выражениям.
Приведу пример:
Можно вынести начальное значение за скобки:
Также можно использовать массивы и итерацию по ним:
Подведём итоги.
Мы разобрали параметры справа. Их разыменование.
Также мы разобрали запись значений в те переменные на которые указывают параметры справа.
Также мы рассмотрели как параметры справа могут использоваться для лямбда-выражений.
Также мы немного коснулись массивов и итерации по ним.
В следующей статье мы разберём параметры слева передаваемые по ссылке и рассмотрим как например реализовать операции такие как += -= и т.п.
Надеюсь, что данная статья была вам полезна.
Там мы рассмотрели ключевые слова :, ;, FUNCTION, PROCEDURE.
А также "параметры слева".
Рассмотрим теперь "параметры справа".
Пусть у нас есть пример с "параметрами слева":
INTEGER FUNCTION Plus INTEGER IN A INTEGER IN B A B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 2 Plus . // - вызываем нашу функцию и печатаем результат
Тут мы имеет "типичную" ОПЗ.
А что делать, если мы хотим пользоваться инфиксной нотацией?
Вот тут нам помогут параметры справа.
Перепишем наш пример с использованием параметров справа:
INTEGER FUNCTION Plus INTEGER IN A // - параметр слева ^ IN B // - параметр СПРАВА передаётся по ССЫЛКЕ, а не по ЗНАЧЕНИЮ. // Его надо разыменовывать. A // - значение параметра A B DO // - разыменовываем значение B. Т.е. зовём метод DO на том слове на которое указывает B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 Plus 2 . // - вызываем нашу функцию ИНФИКСНО и печатаем результат
Подчеркну, что параметры справа передаются по ссылке.
Также можно написать:
1 Plus ( 2 Plus ( 3 Plus 4 ) ) .
Скобки пока обязательны.
Как обойтись без скобок - напишу отдельно.
Также наш пример можно переписать так:
INTEGER FUNCTION Plus INTEGER IN A // - параметр слева ^ IN B // - параметр СПРАВА передаётся по ССЫЛКЕ, а не по ЗНАЧЕНИЮ. // Его надо разыменовывать. A // - значение параметра A B |^ // - разыменовываем значение B. Т.е. зовём метод |^ на том слове на которое указывает B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 Plus 2 . // - вызываем нашу функцию ИНФИКСНО и печатаем результат
Тут используется |^ вместо DO.
Они вообще говоря равноценны.
Про отличия я напишу несколько позже.
Метод |^ в аксиоматике определяется так:
: |^ ^@ IN aRef %SUMMARY 'Разыменовывает параметр слева' ; aRef pop:Word:GetRef DO ; // |^
Детали реализации |^ я также опишу позже.
Но пока отмечу, что |^ использует DO. Т.е. |^ является производным от DO.
Пойдём далее.
Зачем параметры справа передаются по ссылке,а не по значению?
Тому есть много причин.
В частности - "ленивые вычисления".
Рассмотрим реализацию булевских операций AND и OR.
Вот она:
BOOLEAN operator AND BOOLEAN IN aFirst ^ IN aSecond %SUMMARY 'Двусторонний, а не обратный польский &&' ; if aFirst then ( if ( aSecond DO ) then ( true >>> Result ) else ( false >>> Result ) ) else ( false >>> Result ) ; // AND BOOLEAN operator OR BOOLEAN IN aFirst ^ IN aSecond // Двусторонний, а не обратный польский || if aFirst then ( Result := true ) else if ( aSecond DO ) then ( Result := true ) else ( Result := false ) ; // ORТут видно, что параметр aSecond будет вычисляться ТОЛЬКО если он нужен для вычисления всего выражения.
Т.е. если по параметру aFirst - результат выражения будет ещё неясен.
Слово operator является аналогом слов : и FUNCTION. ОН лишь подчёркивает "операторную сущность" определяемых слов.
В частности - операторам можно задавать "приоритет выполнения" как например в Prolog.
Чтобы например избавиться от скобок в примере со словом Plus выше.
Но об этом расскажу отдельно.
Но пока будем считать, что operator определён как:
WordAlias operator : WordAlias OPERATOR :
И что мы получаем с ленивыми вычислениями?
Если написать без ленивых вычислений:
if ( ( anObject <> nil ) ( anObject .SomeMethod ) && ) then
То получим Access Violation.
А с ленивыми вычислениями:
if ( ( anObject <> nil ) AND ( anObject .SomeMethod ) ) then
Access Violation - не будет.
Надеюсь - понятно почему.
Операция <> кстати тоже определена в базовой аксиоматике при помощи правых и левых параметров. И через операцию =.
Вот так:
BOOLEAN operator <> IN aLeft ^ IN aRight %SUMMARY 'Правосторонний, а не обратный польский !=' ; Result := ( aLeft = ( aRight DO ) ! ) ; //<>
Комментировать не буду. Отмечу лишь, что операция ! - это постфиксное отрицание.
Пойдём далее.
Тот факт, что передаётся ссылка на слово, а не значение означает то, что если в качестве слова падали переменную, то мы можем писать в неё.
Реализуем например методы инкремента и декремента.
Так как они описаны в аксиоматике:
VOID operator DEC ^ IN aWhatToDecrement aWhatToDecrement DO // - разыменовываем переменную aWhatToDecrement 1 - // - вычитаем единицу >>>^ aWhatToDecrement // - записываем значение туда куда указывает aWhatToDecrement ; // DEC VOID operator INC ^ IN aWhatToIncrement aWhatToIncrement DO // - разыменовываем переменную aWhatToDecrement 1 + // - прибавляем единицу >>>^ aWhatToIncrement // - записываем значение туда куда указывает aWhatToIncrement ; // INCИ вызов:
INTEGER VAR A // - определяем целочисленную переменную A 0 >>> A // - инициализируем её нулём A . // - печатаем INC A // - увеличиваем A на единицу A . // - печатаем DEC A // - уменьшаем A на единицу A . // - печатаем
Понятное дело, что если мы напишем Inc 1, то мы получим ошибку если не компиляции, то времени исполнения.
Ну и предположим нам надо описать методы IncBy и DecBy.
Вот они:
VOID operator DecBy ^ IN aWhatToDecrement ^ IN aDelta aWhatToDecrement DO // - разыменовываем переменную aWhatToDecrement aDelta DO // - разыменовываем переменную aDelta - // - вычитаем >>>^ aWhatToDecrement // - записываем значение туда куда указывает aWhatToDecrement ; // DecBy VOID operator IncBy ^ IN aWhatToIncrement ^ IN aDelta aWhatToIncrement DO // - разыменовываем переменную aWhatToDecrement aDelta DO // - разыменовываем переменную aDelta + // - прибавляем >>>^ aWhatToIncrement // - записываем значение туда куда указывает aWhatToIncrement ; // IncBy
И вызов:
INTEGER VAR A // - определяем целочисленную переменную A 0 >>> A // - инициализируем её нулём A . // - печатаем IncBy A 2 // - увеличиваем A на 2 A . // - печатаем DecBy A 2 // - уменьшаем A на 2 A . // - печатаем
Пойдём далее.
Параметры справа также удобно использовать для обращения к лямбда-выражениям.
Приведу пример:
: Iteration ^ IN aLambda 0 // - начальное значение 1 aLambda DO 2 aLambda DO 3 aLambda DO 4 aLambda DO 5 aLambda DO 6 aLambda DO 7 aLambda DO 8 aLambda DO 9 aLambda DO 10 aLambda DO ; // Iteration // Вызов: Iteration ( IN A IN B A B + ) . // - просуммирует числа от 0 до 10 и напечатает сумму // Или короче: Iteration + . // - просуммирует числа от 0 до 10 и напечатает сумму
Можно вынести начальное значение за скобки:
: Iteration ^ IN aLambda 1 aLambda DO 2 aLambda DO 3 aLambda DO 4 aLambda DO 5 aLambda DO 6 aLambda DO 7 aLambda DO 8 aLambda DO 9 aLambda DO 10 aLambda DO ; // Iteration // Вызов: 0 Iteration ( IN A IN B A B + ) . // - просуммирует числа от 0 до 10 и напечатает сумму // Или короче: 0 Iteration + . // - просуммирует числа от 0 до 10 и напечатает сумму 1 Iteration * . // - перемножит числа от 1 до 10 и напечатает произведение
Также можно использовать массивы и итерацию по ним:
: Iteration ^ IN aLambda [ 1 2 3 4 5 6 7 8 9 10 ] .for> ( aLambda DO ) ; // Iteration // Вызов: 0 Iteration ( IN A IN B A B + ) . // - просуммирует числа от 0 до 10 и напечатает сумму // Или короче: 0 Iteration + . // - просуммирует числа от 0 до 10 и напечатает сумму 1 Iteration * . // - перемножит числа от 1 до 10 и напечатает произведение
Подведём итоги.
Мы разобрали параметры справа. Их разыменование.
Также мы разобрали запись значений в те переменные на которые указывают параметры справа.
Также мы рассмотрели как параметры справа могут использоваться для лямбда-выражений.
Также мы немного коснулись массивов и итерации по ним.
В следующей статье мы разберём параметры слева передаваемые по ссылке и рассмотрим как например реализовать операции такие как += -= и т.п.
Надеюсь, что данная статья была вам полезна.
Спасибо огромное за статьи, именно то что нужно. Все четко и по делу. Очень интересно. Не забудьте добавить их в базу знаний, сильно помогает в восприятии.
ОтветитьУдалитьВопросы у меня лично остались только по пространствам имен(словари; как работать с глобальным и локальным пространством имен; на что влияет декларация типов при объявлении) в Word'ах. Многое уже описано в предыдущих статьях(точно есть работа со свойствами Word'a снаружи), буду разбираться.
Попробую сделать свою реализацию на коленке, поиграться. Тема для меня правда интересная.
Пожалуйста. Вскорости напишу ещё.
УдалитьНепонятно только зачем что-то своё пытаться делать. Скриптовая машина тем и хороша, что она готовая есть.
Ну это как говорится - каждый "заблуждается" так как сам того хочет :-)
своё делать для практики. Никто и не говорит о полноценной машине, так - немного кода, что бы поиграться и вникнуть в суть. Я же не собираюсь это на практике использовать, когда есть готовое решение..
УдалитьПродолжать кстати тему?
Удалить