Wiki
Clone wikiMindStream / Статьи на русском / Устройство скриптовой машины / PROCEDURE, FUNCTION. Параметры справа и слева.Часть 2
Предыдущая серия была тут - . :, PROCEDURE, FUNCTION. Параметры справа и слева.Часть 1.
Там мы рассмотрели ключевые слова :
, ;
, FUNCTION
, PROCEDURE
.
А также "параметры слева".
Рассмотрим теперь "параметры справа".
Пусть у нас есть пример с "параметрами слева":
#!delphi INTEGER FUNCTION Plus INTEGER IN A INTEGER IN B A B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 2 Plus . // - вызываем нашу функцию и печатаем результат
А что делать, если мы хотим пользоваться инфиксной нотацией?
Вот тут нам помогут параметры справа.
Перепишем наш пример с использованием параметров справа:
#!delphi INTEGER FUNCTION Plus INTEGER IN A // - параметр слева ^ IN B // - параметр СПРАВА передаётся по ССЫЛКЕ, а не по ЗНАЧЕНИЮ. // Его надо разыменовывать. A // - значение параметра A B DO // - разыменовываем значение B. Т.е. зовём метод DO на том слове на которое указывает B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 Plus 2 . // - вызываем нашу функцию ИНФИКСНО и печатаем результат
Также можно написать:
#!delphi 1 Plus ( 2 Plus ( 3 Plus 4 ) ) .
Как обойтись без скобок - напишу отдельно.
Также наш пример можно переписать так:
#!delphi INTEGER FUNCTION Plus INTEGER IN A // - параметр слева ^ IN B // - параметр СПРАВА передаётся по ССЫЛКЕ, а не по ЗНАЧЕНИЮ. // Его надо разыменовывать. A // - значение параметра A B |^ // - разыменовываем значение B. Т.е. зовём метод |^ на том слове на которое указывает B + >>> Result // - складываем A и B и помещаем в Result ; // Plus 1 Plus 2 . // - вызываем нашу функцию ИНФИКСНО и печатаем результат
|^
вместо DO
.
Они вообще говоря равноценны.
Про отличия я напишу несколько позже.
Метод |^
в аксиоматике определяется так:
#!delphi : |^ ^@ IN aRef %SUMMARY 'Разыменовывает параметр слева' ; aRef pop:Word:GetRef DO ; // |^
|^
я также опишу позже.
Но пока отмечу, что |^
использует DO
. Т.е. |^
является производным от DO.
Пойдём далее.
Зачем параметры справа передаются по ссылке,а не по значению?
Тому есть много причин.
В частности - "ленивые вычисления".
Рассмотрим реализацию булевских операций AND
и OR
.
Вот она:
#!delphi 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
Т.е. если по параметру aFirst - результат выражения будет ещё неясен.
Слово operator
является аналогом слов :
и FUNCTION
. ОН лишь подчёркивает "операторную сущность" определяемых слов.
В частности - операторам можно задавать "приоритет выполнения" как например в Prolog.
Чтобы например избавиться от скобок в примере со словом Plus
выше.
Но об этом расскажу отдельно.
Но пока будем считать, что operator определён как:
#!delphi WordAlias operator : WordAlias OPERATOR :
Если написать без ленивых вычислений:
#!delphi if ( ( anObject <> nil ) ( anObject .SomeMethod ) && ) then
То получим Access Violation.
А с ленивыми вычислениями:
#!delphi if ( ( anObject <> nil ) AND ( anObject .SomeMethod ) ) then
Надеюсь - понятно почему.
Операция <>
кстати тоже определена в базовой аксиоматике при помощи правых и левых параметров. И через операцию =
.
Вот так:
#!delphi BOOLEAN operator <> IN aLeft ^ IN aRight %SUMMARY 'Правосторонний, а не обратный польский !=' ; Result := ( aLeft = ( aRight DO ) ! ) ; //<>
!
- это постфиксное отрицание.
Пойдём далее.
Тот факт, что передаётся ссылка на слово, а не значение означает то, что если в качестве слова падали переменную, то мы можем писать в неё.
Реализуем например методы инкремента и декремента.
Так как они описаны в аксиоматике:
#!delphi VOID operator DEC ^ IN aWhatToDecrement aWhatToDecrement DO // - разыменовываем переменную aWhatToDecrement 1 - // - вычитаем единицу >>>^ aWhatToDecrement // - записываем значение туда куда указывает aWhatToDecrement ; // DEC VOID operator INC ^ IN aWhatToIncrement aWhatToIncrement DO // - разыменовываем переменную aWhatToDecrement 1 + // - прибавляем единицу >>>^ aWhatToIncrement // - записываем значение туда куда указывает aWhatToIncrement ; // INC
#!delphi INTEGER VAR A // - определяем целочисленную переменную A 0 >>> A // - инициализируем её нулём A . // - печатаем INC A // - увеличиваем A на единицу A . // - печатаем DEC A // - уменьшаем A на единицу A . // - печатаем
Inc 1
, то мы получим ошибку если не компиляции, то времени исполнения.
Ну и предположим нам надо описать методы IncBy
и DecBy
.
Вот они:
#!delphi 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
#!delphi INTEGER VAR A // - определяем целочисленную переменную A 0 >>> A // - инициализируем её нулём A . // - печатаем IncBy A 2 // - увеличиваем A на 2 A . // - печатаем DecBy A 2 // - уменьшаем A на 2 A . // - печатаем
Параметры справа также удобно использовать для обращения к лямбда-выражениям.
Приведу пример:
#!delphi : 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 и напечатает сумму
#!delphi : 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 и напечатает произведение
#!delphi : 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 и напечатает произведение
Мы разобрали параметры справа. Их разыменование.
Также мы разобрали запись значений в те переменные на которые указывают параметры справа.
Также мы рассмотрели как параметры справа могут использоваться для лямбда-выражений.
Также мы немного коснулись массивов и итерации по ним.
В следующей статье мы разберём параметры слева передаваемые по ссылке и рассмотрим как например реализовать операции такие как +=
-=
и т.п.
Надеюсь, что данная статья была вам полезна.
Updated