среда, 23 декабря 2015 г.

#1163. :, PROCEDURE, FUNCTION. Параметры справа и слева.Часть 1

По мотивам -  http://programmingmindstream.blogspot.ru/2015/12/1162.html?showComment=1450717154802#c256225446808977907

Предыдущая серия была тут - #1149. О кешировании. Поговорим про добавление вложенных элементов.

Ну и тут поступил вопрос про параметры слов (функций) - http://programmingmindstream.blogspot.ru/2015/12/1162.html?showComment=1450717154802#c256225446808977907.

Раз есть вопрос, то постараюсь описать - как определяются слова и их параметры.

Историю коммитов можно посмотреть тут - https://bitbucket.org/lulinalex/mindstream/commits/branch/B284_Inheritance_Try.

Ещё раз оговорюсь:

Идеологически наша скриптовая машина построена на стековой FORTH-машине - https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D1%82_(%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
.

Так что - было бы неплохо ознакомится с языком FORTH.

Для "общего понимания".

Итак.

Простейшее слово определяется так:

: // - признак начала слова
A // - имя слова
 1 >>std::out // - код слова - печатает число 1 или
 1 . // - также печатает число 1
; // - признак конца слова

A // - вызов слова A

Пример можно скопировать в файл Example.script и запустить:

call.ms.script.exe Example.script или
call.ms.script.exe Example.script > a.out

Утилита call.ms.script.exe находится тут - https://bitbucket.org/lulinalex/mindstream/src/0bea4adaed7cbc645faa484fcb38f8aae6562827/Examples/Scripts/call.ms.script.exe?at=B284_Inheritance_Try

Замечение:
Утилита может молча не запускаться, это значит, что её блокирует антивирус. Так как она "получена не из благонадёжного источника".

В этом случае её стоит проверить антивирусом и включить в список разрешённых программ.
Продолжим.

Естественно, как и в любом другом языке программирования, у нас слова могут иметь параметры.

Простейший пример:

: A
  IN aParam // - определяем параметр aParam, слева от нашего слова A
 aParam // - получаем значение параметра
 . // - печатаем значение параметра
: // A

1 A // - вызов нашего слова A с передачей ему ЗНАЧЕНИЯ числа 1 как значения параметра

Можно расширить пример и определить ТИП параметра. Вот так:

: A
  INTEGER IN aParam // - определяем ЦЕЛОЧИСЛЕННЫЙ параметр aParam, слева от нашего слова A
 aParam // - получаем значение параметра
 . // - печатаем значение параметра
: // A

1 A // - вызов нашего слова A с передачей ему ЗНАЧЕНИЯ числа 1 как значения параметра

Тогда в нашу функцию можно будет передать ТОЛЬКО ЦЕЛОЧИСЛЕННЫЕ значения.

Но о типах параметров и переменных мы поговорим чуть позже. В отдельной статье.

В качестве "лирического отступления" рекомендую ознакомиться с описанием "базовой аксиоматики" - https://bitbucket.org/lulinalex/mindstream/src/a071353dbd21d3afaf8f42b774cc890e0f5a74ce/Examples/ScriptedAxiomatics/kwMain.rc.script?at=B284_Inheritance_Try&fileviewer=file-view-default

До сих пор мы рассмотрели ОДИН параметр слева.

Рассмотрим теперь НЕСКОЛЬКО параметров слева.

Пример:

: A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений
 . // - печатаем получившийя результат
; // A

1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2

Хорошо. Мы поговорили про передачу параметров в слово.

А как получить значение из слова?

Разберём этот вопрос.

Простейший пример:

: A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений
 // - тут ничего не печатаем, а просто оставляем полученное значение на стеке
; // A

1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2
. // - печатаем значение со стека, фактически то, что нам вернула функция A

Методика "оставления значения на стеке" используется не только в "допотопном FORTH", но и во вполне себе "современном Ruby" - https://ru.wikipedia.org/wiki/Ruby.

Тут есть один недостаток (он же на самом деле - преимущество) - вызываемая функция может не положить на стек НИЧЕГО, может положить ОДНО значение, а может положить НЕСКОЛЬКО значений.

А вызывающая сторона этот факт не сможет проконтроллировать.

Как быть?

Для этого нам надо определить ТИП ВОЗВРАЩАЕМОГО значения.

Пример:

INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result
: A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений
 >>> Result // - снимаем значение со стека и кладём его в переменную Result.
; // A

1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2
. // - печатаем значение со стека, фактически то, что нам вернула функция A

В данном случае, скриптовая машина ГАРАНТИРУЕТ, что будет возвращено ОДНО целочисленное значение. ОДНО и ТОЛЬКО ОДНО.

Но "есть одно но".

Возврат - скриптовая машина - контроллирует, а вот забор со стека - нет.

И можно написать так.

INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result
: A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений aParam1 и aParam2
 + // - получаем сумму предыдущего значения и того, что лежало на стеке
 >>> Result // - снимаем значение со стека и кладём его в переменную Result.
; // A

1 2 3 A // - вызываем наше слово A для ТРЁХ значений - 1, 2 и 3

Это иногда полезно, но как быть когда хочется "полного контроля"?

Для этого есть "аналоги" слова : - FUNCTION и PROCEDURE.

Пример:

INTEGER // - определяем тип возвращаемого значения и "неявную переменную" Result
FUNCTION // - используем FUNCTION вместо :
 A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений
 >>> Result // - снимаем значение со стека и кладём его в переменную Result.
; // A

1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2
. // - печатаем значение со стека, фактически то, что нам вернула функция A

В данном случае скриптовая машина будет контроллировать как число входящих значений, так и исходящих.

Ну и пример использования слова PROCEDURE:

PROCEDURE // - используем PROCEDURE вместо :
 A
  INTEGER IN aParam1 // - определяем первый параметр
  INTEGER IN aParam2 // - определяем второй параметр
 aParam1 // - получаем значение параметра aParam1
 aParam2 // - получаем значение параметра aParam2
 + // - получаем сумму двух значений
 . // - печатаем полученное значение
; // A

1 2 A // - вызываем наше слово A для двух ЦЕЛОЧИСЛЕННЫХ значений - 1 и 2

Слово PROCEDURE - ГАРАНТИРУЕТ, что наше слово не вернёт НИ ОДНОГО ЗНАЧЕНИЯ.

Подведём итоги.

В данной статье мы рассмотрели ключевые слова :, ;, IN, FUNCTION, PROCEDURE.

А также передачу параметров словам и возврат значений из них.

Также мы слегка коснулись типизации значений.

Кроме типа INTEGER бывают ещё - STRING, OBJECT, CLASS, INTERFACE, CHAR, ARRAY, FILE, BOOLEAN.

А также есть ещё ANY - которое означает "значение любого типа". И PRINTABLE - которое означает "любое значение пригодное для вывода на печать".
А также - VOID, который означеает "гарантированное отсутствие значения".

Также есть ещё типы ITERATABLE, ORDINAL и ATOMIC. О них мы также поговорим позже.

Пока лишь пример из аксиоматики:

INTEGER BOOLEAN TYPE ORDINAL
STRING ORDINAL TYPE ATOMIC
ATOMIC TYPE COMPARABLE

FILE ARRAY TYPE ITERATABLE
ITERATABLE ATOMIC CASTABLE INTERFACE TYPE ANY
ANY TYPE VARIANT

ARRAY TYPE ITERATOR

FUNCTOR TYPE VAR_REF

В следующей статье мы рассмотрим "параметры справа". Зачем они нужны и как ими пользоваться.

Надеюсь, что данная статья была вам полезна.

Комментариев нет:

Отправить комментарий