Сегодня я хотел бы рассказать о языке программирования Red. Язык Red является непосредственным преемником более раннего языка REBOL. Оба они малоизвестны в русскоязычном сообществе, что само по себе странно, т.к. указанные языки представляют интерес как с точки зрения оригинального взгляда на то, каким должно быть программирование, так и тем, что отличаются потрясающей выразительностью, позволяя решать сложные задачи простыми способами.
Данная статья призвана исправить сложившуюся ситуацию, являясь первым пособием по основам языка Red на русском языке.
В 1997 году Карлом Сассенратом, бывшим основным разработчиком AmigaOS, был предложен язык REBOL (http://www.rebol.com/). Однако разработка REBOL прекратилась к 2010 году, и в качестве его преемника в 2011 году Ненадом Ракоцевичем был анонсирован язык Red (http://www.red-lang.org/), наследуя синтаксис родоначальника и призванный его превзойти.
Одним из главных преимуществ Red/REBOL является его исключительная выразительность, позволяющая реализовать заданную функциональность минимальным количеством кода. Следующий график иллюстрирует результат исследования данного показателя для разных языков программирования:
Сравнение выразительности языков программирования (источник)
Из графика видно, что REBOL является самым выразительным языком общего назначения (лидирующие Augeas и Puppet – языки узких предметных областей). Red в ряде случаев является даже более выразительным, чем REBOL.
Другие ключевые отличия и достоинства языка Red:
- Язык полного стека – от низкоуровневого программирования до написания скриптов.
- Создание мультиплатформенных приложений.
- Поддержка кросс-платформенного нативного GUI.
- Легкое создание DSL.
- Функциональное, императивное, реактивное программирование.
- Гомоиконность (способность программы обрабатывать свой код в качестве данных).
- Развитая поддержка макросов.
- Большое число встроенных типов.
- Исключительная простота установки, не требующая инсталляции и настройки.
На текущий момент Red активно развивается, поэтому часть возможностей еще только готовится к реализации. Среди таких возможностей, не реализованных на момент написания данной статьи: поддержка Android и iOS GUI, поддержка в полном объеме операций ввода/вывода, поддержка модульной компиляции, поддержка параллелизма.
Установка программы
Установка Red под платформу Windows:
- Скачайте исполняемый файл, расположенный по адресу (файл весит всего лишь около 1МВ).
- Разместите скачанный файл в выбранную папку.
- Запустите скачанный файл. Выполнится сборка GUI-консоли, которая займет некоторое время (сборка осуществляется лишь при первом запуске).
После того, как сборка выполнится, откроется консоль – Red готов к работе.
Hello World!
Консоль вызывается всякий раз, когда исполняемый файл Red запускается без аргументов. Консоль дает возможность работать с кодом Red в режиме интерпретации.
Для создания первой программы, выводящей текст «Hello world!», введите и выполните в консоли следующий код:
print "Hello World!"
Для создания программы с нативным GUI:
view [text "Hello World!"]
Или немного более сложный вариант:
view [name: field button "Hello world!" [name/text: "Hello world!"]]
Создание исполнимого файла
Для компиляции кода Red в исполнимый файл выполните следующие шаги:
- Введите в текстовом редакторе следующий код (для компиляции, в отличие от интерпретации, код должен обязательно содержать заголовок):
Red [] print "Hello World!"
- Сохраните код в файл
hello.red
в папке, где расположен Red. - В терминале выполните команду (если имя вашего файла компилятора отличается от red, поменяйте его на корректное):
red -c hello.red
В результате выполнения перечисленных шагов программа будет скомпилирована.
Компиляция программы с нативным GUI осуществляется аналогично, однако в заголовок программы для этого требуется внести небольшое изменение:
Red [Needs: 'View]
view [text "Hello World!"]
Диалекты
Red включает в себя ряд диалектов, используемых в зависимости от предметной области:
- Red/Core – основа языка Red.
- Red/System – диалект для программирования на системном уровне.
- Red/View – набор диалектов для визуализации (VID и Draw).
- Red/Parse – диалект, используемый для парсинга.
Далее в статье пойдет речь об основе языка – Red/Core.
Файлы
Файл, содержащий код Red, должен иметь расширение
.red
и кодировку UTF-8.
Заголовки
Программа Red обязательно должна иметь заголовок. Заголовок указывает, что содержащийся в файле код – код Red, а также позволяет задать ряд дополнительных атрибутов.
Для программ Red заголовок в общем случае имеет вид:
Red [block]
Важно! Несмотря на то, что Red нечувствителен к регистру, слово “Red” в заголовке обязательно должно писаться в точности, как показано в примере – с заглавной буквы.
Перечень атрибутов заголовка опционален и определяется самим пользователем.
Минимальный заголовок:
Red []
Стандартный заголовок:
Red [
Title: "Hello World!" ;-- название программы
Version: 1.1.1 ;-- версия программы
Date: 7-Nov-2017 ;-- дата последней модификации
File: %Hello.red ;-- название файла
Author: "John Smith" ;-- имя автора
Needs: 'View ;-- зависимости
]
Комментарии
Программы Red могут содержать комментарии.
Для комментария, состоящего из одной строки, используется точка с запятой:
max: 10 ;-- максимальное значение параметра
Для комментария, состоящего из нескольких строк используются фигурные скобки
{}
. Для того, чтобы быть точно уверенным, что такие комментарии будут восприняты Red как комментарии, а не как код, перед ними следует указать ключевое слово comment
:
{
Это многострочный
комментарий
}
comment {
Это многострочный
комментарий
}
Блоки
Программа, написанная на Red, состоит из блоков, комбинирующих значения (values) и слова (words). Блоки заключаются в квадратные скобки
[]
. Значения и слова в составе блоков всегда разделяются одним или несколькими пробелами – это важно для правильной интерпретации кода.
Блоки используются для кода, списков, массивов и других последовательностей, представляя собой разновидность серий (series).
Примеры блоков:
[white yellow orange red green blue black]
["Spielberg" "Back to the Future" 1:56:20 MCA]
[
"Elton John" 6894 0:55:68
"Celine Dion" 68861 0:61:35
"Pink Floyd" 46001 0:50:12
]
loop 12 [print "Hello world!"]
Значения
Каждое значение в Red имеет свой тип. По умолчанию, наименования типов оканчиваются восклицательным знаком
!
.
Наиболее часто используемые типы:
Тип | Описание | Примеры |
---|---|---|
word! |
Слово программы, рассматриваемое в качестве значения и не требующее выполнения (литерал). | 'print |
block! |
Блок слов программы. | [red green blue] |
integer! |
Целое число. | 1234 |
float! |
Дробное число. | 1.234 |
binary! |
Байтовая строка произвольной длины. | #{ab12345c} |
string! |
Строка. Если строка содержит переносы, кавычки, табуляцию, то она заключается в фигурные скобки {} . |
"Hello world!" |
char! |
Символ. | #"C" |
logic! |
Булевское значение. | true |
time! |
Время. | 12:05:34 |
date! |
Дата. | 07-November-2017 |
tuple! |
Номер версии, значение цвета RGB, сетевой адрес. Должен содержать как минимум три значения, разделенных точками. |
255.255.0.0 |
pair! |
Пара значений для координат, размеров. | 100x200 |
file! |
Название файла, включая путь. | %red.exe |
Слова
Слова в Red должны удовлетворять следующим требованиям:
- Могут включать в себя буквы, цифры и следующие символы:
? ! . ' + - * & | = _ ~
- Не могут содержать символы:
@ # $ % ^ ,
- Не могут начинаться с цифры или быть составлены таким образом, что могут быть интерпретированы как числа.
Слова не чувствительны к регистру и не имеют ограничений по длине.
Слова могут быть записаны четырьмя способами, от чего зависит их интерпретация:
Формат | Комментарий |
---|---|
word |
Возврат значения, которое содержит слово. Если слово содержит функцию, то функция выполняется. |
word: |
Присвоение слову нового значения. |
:word |
Получение значения слова без его выполнения. |
'word |
Рассмотрение слова как символа, без его выполнения. Слово само рассматривается в качестве значения. |
Присвоение значений (создание переменных)
Двоеточие после слова (
:
) используется в качестве оператора присваивания, позволяющего присвоить слову значение. В этом случае слово выступает в качестве переменной.
Слову можно присвоить не только значения простых типов, но и более сложных – функций, блоков или блоков данных:
age: 33
birthday: 11-June-1984
town: "Moscow"
cars: ["Renault" "Peugeot" "Skoda"]
code: [if age > 32 [print town]]
output: function [item] [print item]
Присвоить значение слову/словам можно также с помощью функции
set
:
set 'test 123
print test
; --> 123
set [a b] 123
print [a b]
; --> 123 123
Стоит обратить внимание, что при присваивании значения слову перед этим словом стоит одиночная кавычка, указывающая, что это слово – литерал, и оно не должно выполняться (имеет тип
word!
). Но слова внутри блока не требуют кавычек, т.к. содержимое блока не выполняется без явного указания.
Получение значений
Двоеточие перед словом (
:
) используется для получения значения слова без его выполнения.
Сказанное можно проиллюстрировать следующим примером:
печать: :print
печать "test"
; --> test
В примере содержимое переменной
print
– функции печати – присваивается переменной печать
без выполнения данной функции. Таким образом обе переменные print
и печать
содержат одинаковое значение и это значение – функция печати.
Получить значение слова можно также с помощью функции get
:
печать: get 'print
печать "test"
; --> test
Литералы
Литералы представляют собой слова Red, рассматриваемые в качестве значений, и имеют тип
word!
Литералы можно задать двумя способами: указанием одиночной кавычки перед словом ('
) или размещением слова внутри блока.
word: 'this
print word
; --> this
word: first [this and that]
print word
; --> this
Рассмотренные выше функции
set
и get
требуют литералы в качестве своих аргументов.
Пути
Пути представляют собой набор значений, разделяемых прямым слешем (
/
). Значения в составе пути называются уточнениями (refinements). Пути используются для навигации и поиска.
В зависимости от контекста, пути могут быть использованы для разнообразных целей:
Russia/MO/Podolsk/size | Выбор значения из блока. |
names/12 | Выбор значения из строки по заданной позиции. |
account/balance | Доступ к функции в составе объекта. |
sort/skip | Уточнение действия функции. |
Выполнение выражений
Приоритетность выполнения операций в Red отсутствует, и они по умолчанию выполняются слева направо. Если в выражении операторы смешаны с функциями, то сначала выполняются операторы, а затем функции. Для определения иного порядка выполнения операций используются круглые скобки.
print 5 + 4 * 3
; --> 27
print absolute -3 + 5
; --> 2
print 5 + (4 * 3)
; --> 17
print (absolute -3) + 5
; --> 8
Для выполнения блока используется функция
do
. Особенность функции do
состоит в том, что она возвращает только последнее вычисленное значение:
do [1 + 2]
; --> 3
do [
1 + 2
3 + 4
]
; --> 7
Для того, чтобы вернуть результаты всех выражений, входящих в блок, используется функция
reduce
. Эта функция вычисляет каждое выражение, входящее в блок, и помещает результат вычисления в новый блок:
reduce [
1 + 2
3 + 4
]
; --> [3 7]
Условные операторы
Функция
if
имеет два аргумента – логическое выражение и блок кода. Если логическое выражение имеет значение true
, то блок кода выполняется. Если логическое выражение имеет значения false
или none
, то блок не выполняется, и функция возвращает none
.
a: -2
if a < 0 [print "a - отрицательное число"]
; --> a - отрицательное число
print if a < 0 ["a - отрицательное число"]
; --> a - отрицательное число
Функция
either
похожа на функцию if
с тем отличием, что имеет дополнительный третий аргумент – блок кода, который выполняется в случае, если логическое выражение не соблюдается (т.е. имеет значение false
или none
).
b: 3
either b < 0 [
print "b - отрицательное число"
][
print "b – не отрицательное число"
]
; --> b – не отрицательное число
Функция
any
принимает на вход блок кода и последовательно выполняет входящие в его состав выражения до тех пор, пока не встретит выражение со значением, отличным от false
или none
. В этом случае работа функции any
завершается, и она возвращает найденное значение. В противном случае функция возвращает значение none.
size: 40
if any [size < 20 size > 80] [
print "Значение вне рамок диапазона"
]
; --> Значение вне рамок диапазона
Функция
all
принимает на вход блок кода и последовательно выполняет входящие в его состав выражения до тех пор, пока не встретит выражение со значениями false
или none
. В этом случае работа функции all
завершается, и она возвращает значение none
. В противном случае функция возвращает значение последнего выражения.
size: 40
if all [size > 20 size < 80] [print "Значение в рамках диапазона"]
; --> Значение в рамках диапазона
Условные циклы
Функция
while
имеет два аргумента в виде блоков кода, циклически выполняя их до тех пор, пока первый блок возвращает значение true
. Если первый блок возвращает значение false
или none
, второй блок не выполняется и осуществляется выход из цикла.
a: 1
while [a < 3][
print a
a: a + 1
]
; --> 1
; --> 2
Функция
until
имеет один аргумент в виде блока кода, циклически выполняя его до тех пор, пока блок не вернет значение true
. Блок кода выполняется, как минимум один раз.
a: 1
until [
print a
a: a + 1
a = 3
]
; --> 1
; --> 2
Циклы
Функция
loop
циклически выполняет блок кода заданное число раз, возвращая последнее вычисленное значение.
i: 0
print loop 20 [i: i + 20]
; --> 400
Функция
repeat
циклически выполняет блок кода заданное число раз, возвращая последнее вычисленное значение. В отличии от функции loop
ее первый аргумент служит для контроля за ходом выполнения цикла.
i: 0
print repeat k 10 [i: i + k]
; --> 55
Функция
foreach
позволяет выполнить блок выражений для каждого из элементов заданной серии, предоставляя доступ к каждому из этих элементов.
colors: [red green blue]
foreach color colors [print color]
; --> red
; --> green
; --> blue
Функция
forall
позволяет выполнить блок выражений для каждого из элементов заданной серии, изменяя значение позиции в пределах этой серии.
colors: [red green blue]
forall colors [print first colors]
; --> red
; --> green
; --> blue
Функция
forever
позволяет организовать бесконечный цикл. Выход из такого цикла может быть осуществлен при помощи функции break
.
a: 1
forever [
a: a * a + 1
if a > 10 [print a break]
]
; --> 26
Прерывание цикла
Функция
continue
позволяет прервать выполнение текущей итерации цикла и перейти к следующей итерации.
repeat count 5 [
if count = 3 [continue]
print ["Итерация" count]
]
; --> Итерация 1
; --> Итерация 2
; --> Итерация 4
; --> Итерация 5
Функция
break
позволяет выйти из цикла.
repeat count 5 [
if count = 3 [break]
print ["Итерация" count]
]
; --> Итерация 1
; --> Итерация 2
Выборочное выполнение
Функция
switch
выполняет первый из блоков, соотнесенный с заданным значением. Функция возвращает значение блока, который был выполнен, или none
в обратном случае. Функция также позволяет использовать множество значений для сопоставления.
switch 1 [
0 [print "Ноль"]
1 [print "Единица"]
]
; --> Единица
num: 7
switch num [
0 2 4 6 8 [print "Четное число"]
1 3 5 7 9 [print "Нечетное число"]
]
; --> Нечетное число
Функция
case
выполняет первый из блоков, для которого выполняется соотнесенное с ним условие. Функция возвращает значение блока, который был выполнен, или none в обратном случае.
a: 2
case [
a = 0 [print "Ноль"]
a < 0 [print "Отрицательное число"]
a > 0 [print "Положительное число"]
]
; --> Положительное число
Команды ввода/вывода
Функция
print
позволяет вывести на экран заданное значение. Если значение является блоком, то перед выводом к нему негласно применяется функция reduce
.
print 1234
; --> 1234
print [2 + 3 6 / 2]
; --> 5 3
Функция
prin
практически идентична print
за тем исключением, что после вывода значения не выполняется перенос на новую строку. Увидеть различие в работе можно при выводе в терминал, но не в консоль.
prin "Александр "
prin "Невский"
; --> Александр Невский
Функция
probe
позволяет вывести на экран заданное значение, которое перед выводом преобразуется в строку кода Red.
colors: [red green blue]
probe colors
; --> [red green blue]
Функция
input
позволяет осуществить ввод значения.
prin "Введите ваше имя: "
name: input
print ["Здравствуйте," name]
; --> Здравствуйте, Антон
Серии представляют собой набор значений, упорядоченных в определенном порядке. Большое число типов Red, таких как блоки, строки, пути и т.д., представляют собой серии.
В общем виде серия выглядит следующим образом:
- голова (head) – первая позиция в серии.
- хвост (tail) – позиция, идущая вслед за последним элементом серии.
- позиция – текущая позиция.
По умолчанию, текущая позиция устанавливается на первый элемент в серии.
colors: [red green blue]
print first colors
; --> red
Извлечение значений
Для извлечения значений из серии относительно текущей позиции служат следующие порядковые функции:
first
– значение текущей позиции.second
– значение второй позиции относительно текущей.third
– значение третьей позиции относительно текущей.fourth
– значение четвертой позиции относительно текущей.fifth
– значение пятой позиции относительно текущей.last
– значение последней позиции в серии.
colors: [red green blue yellow cyan black]
print first colors
; --> red
print third colors
; --> blue
print fifth colors
; --> cyan
print last colors
; --> black
Для извлечения значения по номеру позиции можно использовать пути или функцию
pick
.
print colors/3
; --> blue
print pick colors 3
; --> blue
Изменение позиции
Для смещения текущей позиции на одну позицию вперед служит функция
next
. Для изменения текущей позиции требуется изменить значение переменной, ссылающейся на серию.
values: [1 2 3]
print next values
; --> 2 3
print first values
; --> 1
values: next values
print first values
; --> 2
Для смещение текущей позиции на одну позицию назад служит функция
back
.
values: back values
; --> [1 2 3]
print first values
; --> 1
Для смещения сразу на несколько позиций служит функция
skip
. В случае, если смещение имеет положительное значение, то осуществляется смещение вперед, а если отрицательное – назад.
values: [1 2 3]
probe values: skip values 2
; --> [3]
probe values: skip values -2
; --> [1 2 3]
Для смещения непосредственно в голову или на хвост серии служат функции
head
и tail
соответственно (стоит напомнить, что хвостом серии служит позиция, идущая вслед за последним элементом серии).
values: [1 2 3]
probe values: tail values
; --> []
probe values: head values
; --> [1 2 3]
Вставка и добавление значений в серии
Для вставки одного или нескольких значений в серию используется функция
insert
. Значение вставляется на место текущей позиции, при этом текущая позиция не изменяется.
colors: [green blue]
insert colors 'red
probe colors
; --> [red green blue]
С помощью функции
insert
можно осуществлять вставку в произвольное место серии и вставку нескольких значений.
colors: [green blue]
insert next colors 'red
probe colors
; --> [green red blue]
insert colors [silver black]
probe colors
; --> [silver black green red blue]
Работа функции
append
схожа с работой функции insert
с тем отличием, что новое значение или значения всегда добавляются в конец серии.
colors: [green blue]
append colors 'red
probe colors
; --> [green blue red]
Удаление значений из серий
Для удаления одного или нескольких значений серии используется функция
remove
. Удаляется значение, на которое указывает текущая позиция.
colors: [red green blue yellow cyan black]
remove colors
probe colors
; --> [green blue yellow cyan black]
Функция
clear
позволяет удалить все значения серии, начиная с текущей позиции и до ее хвоста. При помощи функции clear
также можно легко очистить всю серию.
colors: [red green blue yellow cyan black]
clear skip colors 3
probe colors
; --> [red green blue]
clear colors
probe colors
; --> []
Изменение значений серий
Для изменения одного или нескольких значений серии используется функция
change
:
colors: [red green blue]
change next colors 'yellow
probe colors
; --> [red yellow blue]
Работа функции
poke
схожа с работой функции change
с тем отличием, что она позволяет явно указать номер позиции относительно текущей, значение которой будет изменено.
colors: [red green blue]
poke colors 2 'yellow
probe colors
; --> [red yellow blue]
Функция
replace
позволяет изменить первое значение в серии, совпадающее с заданным.
colors: [red green blue green]
replace colors 'green 2
; --> [red 2 blue green]
Создание и копирование серий
Функция
copy
позволяет создать новую серию путем копирования существующей. Копирование также важно в случае использования функций, модифицирующих исходную серию, и при желании сохранить исходную серию неизменной.
str: copy "Копируемая строка"
new-str: copy str
blk: copy [1 2 3 4]
str2: uppercase copy "Копируемая строка"
Используя уточнение
/part
, с помощью функции copy
можно скопировать часть серии. В этом случае в качестве аргумента указывается либо число копируемых значений, либо конечная позиция, до которой осуществляется копирование.
colors: [red green blue yellow]
sub-colors: copy/part next colors 2
probe sub-colors
; --> [green blue]
probe copy/part colors next colors
; --> [red]
probe copy/part colors back tail colors
; --> [red green blue]
Поиск в сериях
Функция
find
используется для поиска в серии заданного значения. В случае удачного поиска, функция возвращает позицию, на которой расположено найденное значение. В случае, если заданное значение в серии найдено не было, функция возвращает none
.
a: [1 2 3 4 5]
probe find a 2
; --> [2 3 4 5]
probe find a 7
; --> none
Функция
find
имеет ряд уточнений, позволяющих осуществить более сложный поиск.
Также для поиска может быть полезна функция select
. Ее работа похожа на работу функции find
с тем отличием, что она возвращает не подсерию, а единственное значение серии, следующий за найденным. Это позволяет организовать поиск элементов по сопоставленным с ними значениями.
colors: [red green blue yellow]
print select colors 'green
; --> blue
colors: [
1 red
2 green
3 blue
4 yellow
]
print select colors 2
; --> green
Сортировка серий
Функция
sort
позволяет быстро и легко сортировать серии. Она особенно полезна при сортировке блоков данных, но может также использоваться и для сортировки символов в строке.
names: [Иван Андрей Максим Юрий Вячеслав Дмитрий]
probe sort names
; --> [Андрей Вячеслав Дмитрий Иван Максим Юрий]
print sort [22.8 18 132 57 12 64.9 85]
; --> 12 18 22.8 57 64.9 85 132
print sort "валидация"
; --> аавдиилця
Функция
sort
непосредственно изменяет значение серии, к которой она применяется. Для того, чтобы сохранить исходную серию неизменной, функцию sort
стоит использовать совместно с функцией copy
:
probe sort copy names
По умолчанию сортировка осуществляется по возрастанию. Для изменения направления сортировки используется уточнение
/reverse
:
print sort/reverse [22.8 18 132 57 12 64.9 85]
; --> 132 85 64.9 57 22.8 18 12
В случае, если требуется отсортировать серию, каждая запись которой состоит из нескольких полей, то следует использовать уточнение
/skip
совместно с аргументом, задающим длину каждой записи. По умолчанию сортировка осуществляется по первому значению записи.
colors: [
3 red
4 green
2 blue
1 yellow
]
probe sort/skip colors 2
; --> [
; --> 1 yellow
; --> 2 blue
; --> 3 red
; --> 4 green
; --> ]
Функция
sort
также имеет ряд уточнений, позволяющих организовать более сложную сортировку.Блоки могут содержать в своем составе другие блоки. Вне зависимости от того, сколько значений содержит вложенный блок, он рассматривается как одно значение для внешнего блока. Это определяет правила обращения к значениям блоков для их извлечения.
values: [
123 ["one" "two"]
%file1.red ["test1" ["test2" %file2.txt]]
]
print length? values
4
Массивы
Для реализации массивов используются блоки.
arr: [
[1 2 3 ]
[10 20 30 ]
[a b c ]
]
probe arr/1
; --> [1 2 3]
probe arr/2/2
; --> 20
arr/1/2: 5
probe arr/1
; --> [1 5 3]
arr/2/3: arr/2/2 + arr/2/3
probe arr/2/3
; --> 50
Специальных функций для работы с массивами в Red (пока) нет, однако возможности Red позволяют воспроизвести такую функциональность. В частности, для создания и инициализации массива можно использовать следующий код:
block: copy []
repeat n 5 [append block n]
probe block
; --> [1 2 3 4 5]
Такой подход не требует накладывать ограничения на размер массивов, делая их динамическими.
Создание блоков
Для создания блоков на основе динамических значений используется функция
compose
. В качестве аргумента функции выступает блок, содержащий данные, на основании которых создается новый блок. Выражения в круглых скобках выполняются до того, как новый блок будет создан.
probe compose [(1 + 2) 3 4]
; --> [3 3 4]
probe compose ["Текущее время" (now/time)]
; --> ["Текущее время" 13:28:10]
В Red существует несколько видов функций:
- Нативные (Native) – функции, вычисляемые непосредственно процессором.
- Пользовательские – функции, определяемые пользователем.
- Мезанин-функции (Mezzanine) – функции, являющиеся частью языка, однако не относящиеся к числу нативных.
- Операторы – функции, используемые как инфиксные операторы (например, +, –, * и /)
- Рутины (Routine) – функции, используемые для вызова функций внешних библиотек.
Выполнение функций
Исходя из особенностей синтаксиса Red, важно учитывать порядок выполнения функций и ее аргументов, который является следующим: сначала рассчитываются значения аргументов функции, которые затем подставляются в функцию, после чего осуществляется выполнение функции. При этом расчет аргументов функции выполняется слева направо, т.е. сначала рассчитывается первый аргумент функции, затем второй и т.д. В то же время аргумент функции сам может являться функцией, поэтому допустимы и широко используются выражения следующего вида:
colors: [red green blue]
insert tail insert colors 'yellow 'black
probe colors
; --> [yellow red green blue black]
Уточнения
Уточнения позволяют внести коррективы в стандартное выполнение функции, к которой они применяются. Уточнения также позволяют задать дополнительные аргументы.
Возможно одновременное использование нескольких уточнений для одной функции. Если они требуют дополнительных аргументов, то порядок аргументов определяется порядком записи уточнений, к которым они относятся.
blk: [1 2 3 4 5]
insert/part/dup blk [6 7 8 9] 2 3
probe blk
; --> [6 7 6 7 6 7 1 2 3 4 5]
blk: [1 2 3 4 5]
insert/dup/part blk [6 7 8 9] 2 3
probe blk
; --> [6 7 8 6 7 8 1 2 3 4 5]
Определение функций
Простую пользовательскую функцию, не требующую аргументов, можно создать с помощью функции
does
. Вызов функции по заданному имени вызывает ее выполнение.
print-time: does [print now/time]
print-time
; --> 13:18:13
Если пользовательская функция требует аргументы, то ее можно определить с помощью функции
func
, которая в свою очередь имеет два аргумента:
func spec body
Первый аргумент представляет собой блок, который определяет интерфейс к функции и содержит описание функции, ее аргументы, типы аргументов, описание аргументов. Второй блок представляет собой непосредственно блок, который выполняется при вызове функции.
Использование функций можно показать на примере расчета среднего значения:
average: func [
arg1 arg2
][
sum: arg1 + arg2
sum / 2
]
В приведенном примере сначала определяется пользовательская функция
average
, имеющая два аргумента, задаваемых в первом блоке. Во втором блоке задается тело функции. По умолчанию функция возвращает последнее вычисленное в ее теле значение.
В дальнейшем функция может быть вызвана по имени с указанием ее аргументов.
print average 8 14
; --> 11
Стоит обратить внимание, что в приведенном примере переменная
sum
, определенная в теле функции, по умолчанию становится глобальной и ее значение доступно за пределами функции. Для того, чтобы переменная оставалась локальной, она должна быть описана в блоке, определяющем аргументы функции, с уточнением /local
. После уточнения /local
можно указать целый список переменных, объявляемых локальными:
evaluation: func [
arg1 arg2
/local sum length depth
][
; ... тело функции
]
Пользовательская функция также может быть определена с помощью функции
function
, которая идентична функции func
с тем отличием, что все переменные, определенные в ее теле, по умолчанию являются локальными. Таким образом, при работе с функцией function
не требуется предварительно объявлять локальные переменные с уточнением /local
.
Выход из функций и возврат значений
По умолчанию пользовательская функция возвращает последнее вычисленное в ее теле значение. С помощью функции
return
можно прервать выполнение пользовательской функции в заданной точке и вернуть значение.
iteration: func [i][
repeat count i [if count = 5 [return count]]
none
]
print iteration 3
; --> none
print iteration 7
; --> 5
Для прерывания выполнения пользовательской функции в заданной точке без возврата значения используется функция
exit
.
iteration: func [i][
repeat count i [if count > 5 [print "Stop!" exit]]
none
]
print iteration 7
; --> Stop!
Объекты объединяют значения в едином контексте. Объекты могут включать простые значения, серии, функции и другие объекты.
Создание объектов
Новые объекты создаются с помощью функции
make
, имеющей следующий формат:
new-object: make parent-object new-values
Здесь
new-object
– имя создаваемого нового объекта.Первый аргумент функции,
parent-object
, является родительским объектом, на основании которого создается новый объект. Когда родительского объекта нет, как в случае создания первого объекта заданного типа, то тогда в его качестве указывается тип данных object!
new-object: make object! new-values
Второй аргумент функции,
new-values
, представляет собой блок, который определяет дополнительные переменные и их инициализирующие значения. При создании объекта данный блок выполняется, поэтому может содержать любые выражения для вычисления значений переменных.
Переменные в рамках объекта могут ссылаться в том числе и на функции, определяемые в контексте объекта. Такие функции могут обращаться к другим переменным объекта напрямую, без использования путей.
example: make object! [
var1: 10
var2: var1 + 10
F: func [val][
var1: val
var2: val + 20
]
]
После того, как объект будет создан, он может служить прототипом для создания новых объектов. При этом новые объекты будут являться клонами первого объекта, для переменных которых можно задать собственные значения:
example2: make example []
example3: make example [
var1: 30
var2: var1 + 15
]
Объект, создаваемый на основе другого объекта, можно расширить, добавив в него новые переменные:
example4: make example [
var3: now/date
var4: "Депозит"
]
Доступ к объектам
Доступ к переменным объекта осуществляется с помощью указания путей к ним. Используя пути, можно изменить переменные объекта или выполнить инкапсулированные в него функции:
example/var1: 37
print example/var1
; --> 37
example/F 100
print example/var2
; --> 120
Доступ к переменным объекта также можно получить с помощью функции
in
, которая извлекает соответствующее слово из контекста объекта. Далее с помощью функции set
можно задать значение выбранной переменной, а с помощью функции get
– получить это значение:
print in example 'var1
; --> var1
set in example 'var1 52
print get in example 'var1
; --> 52
Полезные ссылки:
Официальная страница
Полный список функций Red с примерами
Сообщество
Комментариев нет:
Отправить комментарий