rebol document

Глава 9 - Функции

REBOL/Core Руководство пользователя
Основное содержание
Send Us Feedback

Содержание:

1. Обзор
2. Оценка функций
2.1 Аргументы
2.2 Типы данных аргументов
2.3 Уточнения
2.4 Значения функций
3. Определение функций
3.1 Спецификации интерфейса
3.2 Литеральные аргументы
3.3 Получение аргументов
3.4 Определение уточнений
3.5 Локальные переменные
3.6 Локальные переменные, содержащие ряд
3.7 Возвращение значения
3.8 Возврат нескольких значений
4. Вложенные функции
5. Безымянные функции
6. Условные функции
7. Атрибуты функций
8. Прямые ссылки
9. Область переменных
10. Отражающие свойства
11. Справка по функциям в Интернете
12. Просмотр исходного кода

1. Обзор

REBOL предоставляет несколько видов функций:

 NativeФункция, которая оценивается непосредственно процессором. Это функции самого нижнего уровня языка.
 FunctionФункция более высокого уровня, которая определяется блоком и оценивается путём оценки функций внутри блока. Также называется пользовательскими функциями.
 MezzanineИмя для функций более высокого уровня, которые являются стандартной частью языка. Это не собственные функции.
 OperatorФункция, которая используется как инфиксный оператор. Примеры +, -, * и /.
 RoutineФункция, которая используется для вызова функций внешней библиотеки (функция REBOL/Command).

2. Оценка функций

В главе «Выражения»были описаны общие детали оценки. Способ вычисления аргументов функции определяет общий порядок слов и значений в языке. В этом разделе более подробно рассказывается о том, как оцениваются функции.

2.1 Arguments

отправить другу ** Ошибка сценария: для send отсутствует аргумент сообщения. ** Куда: отправить друга Если указано слишком много аргументов, дополнительные значения игнорируются.

Функции получают аргументы и возвращают результаты. Большинству функций требуется один или несколько аргументов; хотя некоторые функции, например now (текущая дата и время), не требуют аргументов вовсе.

Аргументы, которые передаются функции, обрабатываются интерпретатором и затем передаются в функцию. Аргументы обрабатываются одинаково, независимо от типа вызываемой функции, будь то собственная функция, оператор, пользовательская функция или иное. Например, функция send (отправить) ожидает два аргумента:

приятель: luke@rebol.com
сообщение: "Записка в бутылке"

send приятель сообщение

Сначала оценивается слово «приятель», и его значение (luke@rebol.com) предоставляется в качестве первого аргумента для send. Затем оценивается слово «сообщение», и его значение становится вторым аргументом. Представьте, что значения переменных «приятель» и «сообщение» подставляются в строку перед send (отправить):

send luke@rebol.com "Записка в бутылке"

Если вы предоставите слишком мало аргументов функции, будет возвращено сообщение об ошибке. Например, функция отправки ожидает два аргумента, и если вы отправляете один, возвращается ошибка.

send приятель
** Ошибка сценария: для send отсутствует аргумент сообщения.
** Где: send приятель

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

send приятель сообщение "что-то ещё"

В предыдущем примере send уже имеет два аргумента, поэтому строка, которая является третьим аргументом, игнорируется. Обратите внимание, что сообщение об ошибке не появляется. В этом случае не было функций, ожидающих третьего аргумента. Однако в некоторых случаях третий аргумент может принадлежать другой функции, которая была оценена перед send.

Аргументы функции оцениваются слева направо. Этот порядок соблюдается даже тогда, когда аргументы сами являются функциями. Например, если вы напишете:

send приятель detab copy сообщение

второй аргумент должен быть вычислен путём вычисления функции detab и функции copy. Результат копирования будет передан в detab, а результат detab будет передан в send. В предыдущем примере функция copy (копирование) принимает единственный аргумент, сообщение, и возвращает его копию. Скопированное сообщение передаётся в функцию detab, которая удаляет символы табуляции и возвращает отредактированное (без символов табуляции) сообщение, которое передаётся в функцию send. Обратите внимание на то, как результаты функций перетекают справа налево при вычислении выражения.

Происходящую здесь оценку можно показать с помощью скобок, чтобы пояснить, что оценивается в первую очередь. (Однако круглые скобки не обязательны и фактически немного замедляют оценку.)

send приятель (detab (copy сообщение))

Каскадный эффект результатов, передаваемых функциям, весьма полезен. Вот пример, в котором дважды используется insert (вставка) в одном и том же выражении:

file: %image
insert tail insert file %graphics/ %.jpg
print file
graphics/image.jpg

В следующем примере к основному имени файла добавляются имя каталога и суффикс. Скобки можно использовать для пояснения порядка оценки:

insert (tail (insert file %graphics/)) %.jpg

Примечание о круглых скобках

Скобки - это хорошие «обучающие колеса», чтобы начать писать REBOL. Однако не пройдёт много времени, прежде чем вы сможете отказаться от этой помощи и писать выражения прямо без скобок. Отсутствие круглых скобок позволяет интерпретатору быстрее вычислять выражения.

2.2 Типы данных аргумента

Функции обычно требуют аргументов определённого типа данных. Например, первым аргументом функции send может быть только адрес электронной почты или блок адресов электронной почты. Любой другой тип значения вызовет ошибку:

send 1234 "цифры"
** Ошибка сценария: send ожидает адресный аргумент типа: блок электронной почты.
** Где: send 1234 "цифры"

В предыдущем примере сообщение об ошибке сообщает вам, что аргумент адреса функции send должен быть либо адресом электронной почты, либо блоком.

Чтобы быстро узнать, какие типы аргументов принимает функция, введите в командной строке консоли следующее:

help send
ИСПОЛЬЗОВАНИЕ:
    SEND адрес сообщение /only /header header-obj
ОПИСАНИЕ:
    Отправляет "сообщение" на указанный "адрес" (или блок с адресами)
    SEND is a function value.
АРГУМЕНТЫ:
    адрес -- Адрес электронной почты (e-mail) или блок с адресами (Тип: email блок)
    сообщение -- Текст сообщения. Первая строка это тема письма. (Тип: любой)
УТОЧНЕНИЯ:
    /only -- отправить только одно сообщение на несколько адресов.
    /header -- указать свой собственный заголовок
        header-obj - заголовок для использования (Тип: объект)

В разделе АРГУМЕНТЫ указывается тип данных каждого аргумента. Обратите внимание, что второй аргумент может иметь любой тип данных. Итак, можно написать:

send luke@rebol.com $1000.00

2.3 Уточнения

Уточнение определяет вариант нормальной оценки функции. Уточнения также позволяют указывать необязательные аргументы. Уточнения доступны как для собственных, так и для пользовательских функций.

Уточнения указываются после имени функции с дробной чертой (/) и имени уточнения. Например:

copy/part  (копировать только часть строки)

find/tail  (вернуть хвост совпадения)

load/markup  (возврат XML/HTML тегов и строк)

Функции также могут включать несколько уточнений:

find/case/tail (совпадение регистра и возврат хвоста)

insert/only/dup (вставить весь блок несколько раз)

Вы видели функцию copy (копирование), используемую для копирования строки. По умолчанию copy возвращает копию своего аргумента:

строка: "нет такого времени, как настоящее"
print copy строка
нет такого времени, как настоящее

Используя уточнение /part, функция copy возвратит часть строки:

print copy/part строка 10
нет такого

В предыдущем примере уточнение /part указывает, что копируются только десять символов строки.

Чтобы узнать, какие уточнения разрешены для такой функции, как copy, используйте встроенную справку:

help copy
ИСПОЛЬЗОВАНИЕ:
    COPY значение /part диапазон /deep
ОПИСАНИЕ:
     Возвращает копию "значения".
     COPY is an action value.
АРГУМЕНТЫ:
     значение - Обычно серия (Тип: битовый набор последовательного порта)
УТОЧНЕНИЯ:
     /part -- Ограничивает заданную длину или позицию.
         диапазон - (Тип: номер серия порт)
     /deep -- также копирует значения серий внутри блока.

Обратите внимание, что для уточнения /part требуется дополнительный аргумент. Не все уточнения требуют дополнительных аргументов. Например, уточнение /deep указывает, что copy делает копии всех своих подблоков. Никаких других аргументов не требуется.

Когда с функцией используется несколько уточнений, порядок дополнительных аргументов определяется порядком, в котором указаны уточнения. Например:

str: "test"
insert/dup/part str "this one" 4 5
print str
this this this this test

При изменении порядка уточнения /dup и /part изменяется порядок аргументов. Вы можете увидеть разницу:

str: "test"
insert/part/dup str "this one" 4 5
print str
thisthisthisthisthistest

Уточнения указывают порядок аргументов.

2.4 Значения функций

Предыдущие примеры описывают, как функции возвращают значения при их оценке. Однако иногда вы хотите получить функцию как значение, а не возвращаемое значение. Это можно сделать, поставив перед именем функции двоеточие или используя функцию get. Например, чтобы установить слово pr для функции print (печать), вы должны написать:

pr: :print

Вы также можете написать:

pr: get `print

Теперь pr эквивалентна функции print:

pr "this is a test"
this is a test

3. Определение функций

Вы можете определять функции, которые работают так же, как собственные функции. Это называются пользовательскими функциями. Пользовательские функции относятся к типу данных function!.

Вы можете создавать простые функции, не требующие аргументов, с помощью функции does. В этом примере определяется новая функция, которая печатает текущее время:

print-time: does [print now/time]
print-time
10:30

Функция does возвращает значение, которое является новой функцией. В этом примере слово print-time установлено на функцию. Однако это значение функции может быть установлено в слово, передано другой функции, возвращено как результат функции, сохранено в блоке или немедленно оценено.

Функции, требующие аргументов, выполняются с помощью функции func, которая принимает два аргумента:

func spec body

Первый аргумент - это блок, определяющий интерфейс функции. Он включает описание функции, её аргументов, типы, разрешённые для аргументов, описания аргументов и другие элементы. Второй аргумент - это блок кода, который оценивается всякий раз, когда оценивается функция.

Вот пример новой функции под названием sum::

sum: func [arg1 arg2] [arg1 + arg2]

Вновь определённая функция принимает два аргумента, как указано в первом блоке. Второй блок - это тело функции, которая при вычислении складывает два аргумента вместе. Новая функция возвращается как значение от func, и ей присваивается слово sum. Вот он в употреблении:

print sum 123 321
444

Результат сложения arg1 и arg2 возвращается и печатается.

Func определён в REBOL

Func - это функция, которая выполняет другие функции. Он выполняет тип данных function!. Func определяется как:

func: make function! [args body] [
    make function! args body
]

3.1 Спецификации интерфейса

Первый блок определения функции называется спецификацией интерфейса. Этот блок включает описание функции, её аргументов, типы данных, разрешённые для аргументов, описания аргументов и другие элементы.

Спецификация интерфейса - это диалект REBOL (потому что у него другие правила оценки, чем у обычного кода). Блок спецификации имеет формат:

[
    "описание функции"
    [необязательные атрибуты]

    аргумент-1 [необязательный-тип]
    "описание аргумента"

    аргумент-2 [необязательный-тип]
    "описание аргумента"

    ...

    /уточнение
    "описание уточнения"

    уточнение-аргумент-1 [необязательный-тип]
    "описание аргумента уточнения"

    ...
]

Поля блока спецификации:

 ОписаниеКраткое описание функции. Это строка, к которой могут обращаться другие функции, такие как help (помощь), чтобы вывести описания функций.
 АтрибутыБлок, описывающий особые свойства функции, например её поведение при ошибках. В будущем он может быть расширен за счёт включения флагов для оптимизации.
 АргументПеременная, которая используется для доступа к аргументу из тела функции.
 Тип аргументаБлок, который определяет типы данных, которые принимает функция. Если в функцию передаётся тип данных, не идентифицированный в этом блоке, произойдёт ошибка.
 Описание аргументаКраткое описание аргумента. Как и описание функции, это может быть доступно другим функциям, таким как help.
 УточнениеУточняющее слово, указывающее на особое поведение функции.
 Описание уточненияКраткое описание уточнения.
 Аргумент уточненияПеременная, используемая при уточнении.
 Тип аргумента уточненияБлок, который определяет типы данных, которые принимаются уточнением.
 Описание аргумента уточненияКраткое описание аргумента уточнения.

Все эти поля необязательны.

Например, блок аргументов функции суммы (определённой в предыдущем примере) расширен, чтобы ограничить тип принимаемых аргументов. Он также включает описание функции и её ожидаемых аргументов.

sum: func [
    "Возвращает сумму двух чисел."
    arg1 [number!] "первое число"
    arg2 [number!] "второе число"
][
    arg1 + arg2
]

Теперь тип данных аргументов автоматически проверяется, выявляя такие ошибки, как:

print sum 1 "test"
** Ошибка скрипта: sum ожидает аргумент arg2 типа: число.
** Где: print sum 1 "test"

Чтобы разрешить дополнительные типы данных аргумента, можно указать их несколько разделив пробелом:

sum: func [
    "Возвращает сумму двух чисел."
    arg1 [number! tuple! money!] "первое число"
    arg2 [number! tuple! money!] "второе число"
][
    arg1 + arg2
]

print sum 1.2.3 3.2.1
4.4.4
print sum $1234 100
$1334.00

Теперь функция sum принимает в качестве аргументов число (number!), кортеж (tuple!) или денежное (money!) значение. Если внутри функции вам нужно различать, какой тип данных был передан, вы можете использовать функции проверки типа данных:

if tuple? arg1 [print arg1]

if money? arg2 [print arg2]

Поскольку функция sum предоставила строки описания, функция help теперь предоставляет полезную информацию о ней:

help sum
ИСПОЛЬЗОВАНИЕ:
    SUM arg1 arg2
ОПИСАНИЕ:
     Возвращает сумму двух чисел.
     SUM is a function value.
АРГУМЕНТЫ:
     arg1 -- первое число (Тип: номер кортежа денег)
     arg2 -- второе число (Тип: номер кортежа денег)

3.2 Буквальные аргументы

Как описано ранее, интерпретатор оценивает аргументы функций и передаёт их в тело функции. Однако бывают случаи, когда вы не хотите, чтобы аргументы функции оценивались. Например, если вам нужно передать слово и получить к нему доступ из тела функции, вы не хотите, чтобы оно оценивалось как аргумент. Функция help, ожидающая слова, является хорошим примером:

help print

Чтобы предотвратить оценку print, функция help должна указать, что её аргумент не должен оцениваться.

Чтобы указать, что аргумент не оценивается, поставьте перед именем аргумента одинарную кавычку (обозначает буквальное слово). Например:

zap: func [`var] [set var 0]

test: 10
zap test
print test
10

Аргумент var указан с одиночной кавычки, который инструктирует интерпретатор, что это аргумент передаётся без оценки перед передачей. Аргумент передаётся как слово. Например:

say: func [`var] [probe var]
say test
test

В примере печатается слово, переданное в качестве аргумента.

Другой пример - функция, которая увеличивает переменную на единицу и возвращает ее результат (аналогично функции приращения ++ в C):

++: func ['word] [set word 1 + get word]

count: 0
++ count
print count
1
print ++ count
2

3.3 Получение аргументов

Аргументы функции также могут указывать, что значение слова выбирается, но не оценивается. Это похоже на буквальные аргументы, описанные выше, но вместо передачи слова значение слова передаётся без оценки.

Чтобы указать, что аргумент должен быть выбран, но не вычислен, перед именем аргумента поставьте двоеточие. Например, следующая функция принимает функции в качестве аргументов:

print-body: func [:fun] [probe second :fun]

Функция-образец печатает тело переданной ей функции. Аргументу предшествует двоеточие, которое указывает, что значение слова должно быть получено, но не оцениваться в дальнейшем.

print-body reform
[form reduce value]
print-body rejoin
[
    if empty? block: reduce block [return block]
    append either series? first block [copy first block] [
        form first block] next block
]

3.4 Определение уточнений

печатная сумма / среднее 123 321 222 Чтобы указать уточнение, которое принимает дополнительные аргументы, следуйте уточнению с определениями аргументов: Я func [ «Вернуть сумму двух чисел». arg1 [число!] "первое число" arg2 [число!] "второе число" / раз "умножить результат" количество [число!] "сколько раз" ][ в любом случае [arg1 + arg2 * amount] [arg1 + arg2] ] Сумма действительна только тогда , когда времена изысканности верно. Вот пример: напечатать сумму / раз 123 321 10 4440

Уточнения могут использоваться для указания вариации в обычном вычислении функции, а также для предоставления необязательных аргументов. Уточнения добавляются в блок спецификации функции в виде слова, которому предшествует дробная черта (/).

В теле функции уточняющее слово используется как логическое значение, чтобы определить, было ли уточнение предоставлено при вызове функции.

Например, следующий код добавляет уточнение к функции sum, которая была определена в предыдущем примере:

sum: func [
    "Возвращает сумму двух чисел."
    arg1 [number!] "первое число"
    arg2 [number!] "второе число"
    /average "вернуть среднее значение чисел"
][
    either average [arg1 + arg2 / 2][arg1 + arg2]
]

Функция sum указывает уточнение /average (среднее). В теле функции слово проверяется функцией either (либо), которая возвращает истину, если указано уточнение.

print sum/average 123 321
222

Чтобы указать уточнение, которое принимает дополнительные аргументы, следуйте уточнению с определениями аргументов:

sum: func [
    "Возвращает сумму двух чисел."
    arg1 [number!] "первое число"
    arg2 [number!] "второе число"
    /times "умножить результат"
    amount [number!] "сколько раз"
][
    either times [arg1 + arg2 * amount][arg1 + arg2]
]

Сумма умножится (amount) только тогда, когда указанно times верно. Вот пример:

print sum/times 123 321 10
4440

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

3.5 Локальные переменные

Локальная переменная - это слово, значение которого определяется в рамках функции. Изменения локальной переменной влияют только на функцию, в которой определена переменная. Если то же слово используется вне функции, на него не повлияют изменения в локальной переменной с тем же именем.

Переменные аргумента и уточнения являются локальными переменными. Их значения определяются в рамках функции. По соглашению дополнительные локальные переменные могут быть указаны с уточнением /local. За уточнением /local следует список слов, которые используются в качестве локальных переменных в функции.

average: func [
    block "Block of numbers"
    /local total length
][
    total: 0
    length: length? block
    foreach num block [total: total + num]
    either length > 0 [total / length][0]
]

Здесь слова total и length являются локальными для функции.

Другой метод создания локальных слов - использовать функцию function, которая идентична func , но принимает отдельный блок, содержащий локальные слова:

average: function [
    block "Block of numbers"
][
    total length
][
    total: 0
    length: length? block
    foreach num block [total: total + num]
    either length > 0 [total / length][0]
]

В этом примере обратите внимание, что уточнение /local не используется с функцией function. Функция function создаёт уточнения для вас.

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

>> b: function [c][a][a: c / 2 c + a]
>> a: 100
== 100

Переменная a равна 100, а внутри функции b ей присваивается новое значение, однако, т.к. оно объявлено локальным, то перед вызовом функции b интерпретатор запомнит текущее значение переменной a и по завершению функции востановит его:

>> print b 3
4.5
>> print a
100

Если не указывать a как локальную переменную внутри функции b, то её значение не сохраниться перед вызовом функции b

>> b: func [c][a: c / 2 c + a]
>> print b 3
4.5
>> print a
1.5

Переменные, указанные в качестве аргументов функций всегда являются локальными, для функции, т.е. если у них есть значения, то после оценки функции они останутся прежними, невзирая но то, что внутри функции они имеют другие значения

>> c: 123
== 123
>> print b 3
4.5
>> print c
123

3.6 Локальные переменные, содержащие серии

Локальные переменные, содержащие серии, необходимо скопировать, если серия используется несколько раз. Например, если вы хотите, чтобы строка stars была одинаковой каждый раз, когда вы вызываете функцию start-name, вы должны написать:

star-name: func [name] [
    stars: copy "**"
    insert next stars name
    stars
]

В противном случае, если вы напишете:

star-name: func [name] [
    stars: "**"
    insert next stars name
    stars
]

вы будете использовать одну и ту же строку каждый раз, и каждый раз, когда используется функция, в результате будет появляться предыдущее имя.

print star-name "test"
*test*
print star-name "this"
*thistest*

Это важно

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

3.7 Возврат значения

Как вы знаете из главы о выражениях, блоки возвращают своё последнее значение, когда они оцениваются:

do [1 + 3  5 + 7]
12

Это также верно для функций. Последнее значение возвращается как значение функции:

sum: func [a b] [
    print a
    print b
    a + b
]

print sum 123 321
123
321
444

Кроме того, функцию return можно использовать, чтобы остановить оценку функции в любой момент и вернуть значение:

find-value: func [series value] [
    forall series [
        if (first series) = value [
            return series
        ]
    ]
    none
]

probe find-value [1 2 3 4] 3
[3 4]

В этом примере, если значение найдено, функция возвращает серию в позиции совпадения. В противном случае функция возвращает none.

Чтобы остановить оценку функции без возврата значения, используйте функцию exit:

source1: func [
    "Print the source code for a word"
    'word [word!]
][
    prin join word ": "
    if not value? word [print "undefined" exit]
    either any [
        native? get word op? get word action? get word
    ][
        print ["native" mold third get word]
    ][print mold get word]
]

3.8 Возврат нескольких значений

Чтобы вернуть более одного значения из функции, используйте блок. Вы можете сделать это легко, вернув блок, который был уменьшен.

Например:

find-value: func [series value /local count] [
    forall series [
        if (first series) = value [
            reduce [series  index? series]
        ]
    ]
    none
]

Функция возвращает блок, содержащий серию и значение индекса, в котором было найдено значение.

probe find-value [1 2 3 4] 3
[[3 4] 3]

Функция reduce была необходима для возврата нескольких значений из функции для версии 2.3 включительно. Для последней версии Rebol 2.7, можно её не использовать:

>> f: func [a] [ reduce [ a + a a * a ] ]
>> print f 3
6 9
>> f: func [a] [ [ a + a a * a ] ]
>> print f 4
8 16

Чтобы легко установить переменные в возвращаемое значение функции, используйте set:

set [block index] find-value [1 2 3 4] 3
print block
3 4
print index
3

4. Вложенные функции

Например, чтобы создать глобальную функцию внутри функции, присвойте её глобальной переменной: make-timer: func [код] [ таймер: код функции [время] ] make-timer [время ожидания] таймер 5 do-timer: func [задержка кода / местный таймер] [ таймер: код функции [время] задержка таймера задержка таймера ] do-timer [время ожидания] 5 Чтобы вернуть функцию в результате: make-timer: func [код] [ func [временной] код ] timer: make-timer [время ожидания] таймер 5 Используйте правильные локальные переменные make-timer: func [задержка кода] [ таймер: func [время] [время ожидания + задержка] ]

Функции могут определять другие функции. Подфункции могут быть глобальными, локальными или возвращаться в результате, в зависимости от их назначения.

Например, чтобы создать глобальную функцию внутри функции, присвойте её глобальной переменной:

make-timer: func [code] [
    timer: func [time] code
]
make-timer [wait time]
timer 5

Чтобы создать локальную функцию, присвойте её локальной переменной:

do-timer: func [code delay /local timer] [
    timer: func [time] code
    timer delay
    timer delay
]
do-timer [wait time] 5

Функция timer существует только в период, когда оценивается функция do-timer.

Чтобы вернуть функцию в результате:

make-timer: func [code] [
    func [time] code
]
timer: make-timer [wait time]
timer 5

Используйте правильные локальные переменные

Вам следует избегать использования переменных, которые являются локальными для функции верхнего уровня, в качестве неоцененной части вложенной функции. Например:

make-timer: func [code delay] [
    timer: func [time] [wait time + delay]
]

В этом примере слово delay динамически принадлежит функции make-timer. Этого следует избегать, поскольку значение delay будет изменяться при последующих вызовах make-timer.

5. Безымянные функции

Имена функций - это переменные. В REBOL переменная - это переменная, независимо от того, что она содержит. В функциональных переменных нет ничего особенного.

Кроме того, функциям не требуются имена. Вы можете создать функцию и сразу оценить её, сохранить в блоке, передать в качестве аргумента функции или вернуть её как результат функции. Такие функции безымянны.

Вот пример, который создаёт блок безымянных функций:

funcs: []
repeat n 10 [
    append funcs func [t] compose [t + (n * 100)]
]
print funcs/1 10
110
print funcs/5 10
510

Функции также можно создавать и передавать другим функциям. Например, когда вы используете sort с вашим собственным сравнением, вы предоставляете функцию в качестве аргумента:

sort/compare data func [a b] [a > b]

6. Условные функции

Поскольку функции создаются динамически путём оценки, вы можете определить, как вы хотите создать функцию, на основе другой информации. Это способ предоставить условный код, который находится в подъязыках макросов или препроцессоров других языков программирования. В языке REBOL этот тип условного кода выполняется с помощью обычного кода REBOL.

Например, вы можете создать отладочную версию функции, которая выводит дополнительную информацию:

test-mode: on

timer: either test-mode [
    func [delay] [
        print "delaying..."
        wait delay
        print "resuming"
    ]
][
    func [delay] [wait delay]
]

Здесь вы создадите одну из двух функций в зависимости от того, в каком тестовом режиме вы работаете. Это также можно записать короче:

timer: func [delay] either test-mode [[
    print "delaying..."
    wait delay
    print "resuming"
]][[wait delay]]

7. Функциональные атрибуты

Атрибуты функции обеспечивают контроль над определённым поведением функции, например, методом, который функция использует для обработки ошибок или для выхода. Атрибуты - это необязательный блок слов в спецификациях интерфейса.

В настоящее время существует два атрибута функции: catch и throw.

Сообщения об ошибках обычно отображаются, когда они возникают внутри функции. Если указан атрибут catch , ошибки, возникающие внутри функции, автоматически перехватываются функцией. Ошибки отображаются не внутри функции, а в той точке, где функция использовалась. Это полезно, если вы предоставляете библиотеку функций (мезонинные функции) и не хотите, чтобы ошибка отображалась внутри вашей функции, а там, где она была вызвана:

root: func [[catch] num [number!]] [
    if num < 0 [
        throw make error! "только положительные числа"
    ]
    square-root num
]

root 4
2
root -4
** Ошибка пользователя: только положительные числа
** Где: root -4

Обратите внимание, что в этом примере ошибка возникает там, где был вызван root, даже если фактическая ошибка была сгенерирована в теле функции. Это потому, что использовался атрибут catch .

Без атрибута catch ошибка возникла бы в функции square-root:

root: func [num [number!]] [
    square-root num
]
root -4
** Математическая ошибка: требуется положительное число.
** Где: square-root num

Пользователь может ничего не знать о внутреннем устройстве функции root. Таким образом, сообщение об ошибке может сбивать с толку. Пользователь знает только о root, но ошибка была с square-root.

Не путайте атрибут catch с функцией catch . Хотя они похожи, функция catch может быть применена к любому блоку, который оценивается.

Атрибут throw позволяет создавать свои собственные функции управления, такие как for, foreach, if, loop, и forever, позволяя ваши функции передать return и exit операции. Например, эта функция цикла:

loop-time: func [time block] [
    while [now/time < time] block
]

оценивает блок до тех пор, пока не наступит или не пройдёт определённое время. Затем этот цикл можно использовать в функции:

do-job: func [job][
    loop-time 10:30 [
        if error? try [page: read http://www.rebol.com]
            [return none]
    ]
    page
]

Что же происходит при вычислении блока [return none]? Поскольку этот блок оценивается функцией loop-time, возврат происходит в этой функции, а не в do-job.

Этого можно избежать атрибутом throw:

loop-time: func [[throw] time block] [
    while [now/time < time] block
]

Атрибут throw вызывает return или exit, который произошёл в блок, чтобы быть выброшен до прежнего уровня, что следующая функция вызываетdo-job для возврата.

8. Перспективные ссылки

Иногда скрипту необходимо обратиться к функции до того, как она будет определена. Это может быть сделано до тех пор, пока переменная для функции не вычисляется до её определения.

buy: func [item] [
    append own item
    sell head item   ; появляется до того, как он будет определён
]

sell: func [item] [
    remove find own item
]

9. Объем переменных

Контекст переменных называется их областью действия. Широкий диапазон переменных - это глобальные и локальные. REBOL использует форму статической области видимости, которая называется определением области действия. Область действия переменной определяется при определении её контекста. В случае функции это определяется тем, когда функция определена.

Все локальные переменные, определённые в функции, имеют область видимости относительно этой функции. Вложенные функции и объекты могут получить доступ к словам своих родителей.

a-func: func [a] [
    print ["a:" a]
    b-func: func [b] [
        print ["b:" b]
        print ["a:" a]
        print a + b
    ]
    b-func 10
]
a-func 11
a: 11
b: 10
a: 11
21

Обратите внимание, что b-func имеет доступ к переменной a-func.

Слова, привязанные вне функции, сохраняют эти привязки даже при вычислении внутри функции. Это результат статической области видимости, и он позволяет вам писать свои собственные функции оценки блоков (например if, while, loop).

Например, вот функция if, которая оценивает один из трёх блоков на основе знака условного значения:

ifs: func [
    "Если значение положительное, то выполнится block 1, равно нулю block 2, отрицательное ,block 3"
    condition block1 block2 block3
][
    if positive? condition [return do block1]
    if negative? condition [return do block3]
    return do block2
]

print ifs 12:00 - now/time ["утро"]["полдень"]["вечер"]
вечер

Переданные блоки могут содержать те же слова, которые используются в функции ifs, не влияя на слова, определённые локально для функции. Это потому, что слова, переданные в функцию, не связаны с функцией.

Следующий пример передаёт слова block1, block2 и block3 к ifs, как предварительно определённых слов. Функция ifs не путается между словами, переданными в качестве аргументов, и словами с тем же именем, определёнными локально:

block1: "сейчас утро"
block2: "только что миновал полдень"
block3: "вечернее время"

print ifs (12:00 - now/time) [block1][block2][block3]
вечернее время

10. Отражающие свойства.

Спецификация всех функций может быть получена и изменена во время выполнения. Например, вы можете распечатать блок спецификации для функции с помощью:

probe third :if
[
    "Если условие ИСТИНА, оценивает блок"
    condition
    then-block [block!]
    /else "Если не верно, оценить этот блок"
    else-block [block!]
]

Основной код функций можно получить с помощью:

probe second :append
[
    head either only [
        insert/only tail series :value
    ][
        insert tail series :value
    ]
]

Функции могут запрашиваться динамически во время оценки. Так работают функции help (справки) и source (исходный код) и как форматируются сообщения об ошибках.

Кроме того, эта функция полезна для создания ваших собственных уникальных версий существующих функций. Например, может быть создана определяемая пользователем функция print (печать), которая имеет точно такую ​​же спецификацию, что и print, но отправляет свой вывод в строку, а не на дисплей:

output: make string! 1000

print-str: func third :print [
    repend output [reform :value newline]
]

Имя аргумента, используемого для print-str, берётся из спецификации интерфейса для print. Вы можете изучить эту спецификацию с помощью:

probe third :print
[
    «Выводит значение, за которым следует разрыв строки».
    value «Значение для печати»
]

11. Встроенная справка

Полезную информацию обо всех функциях системы можно получить с помощью функции help:

help send
ИСПОЛЬЗОВАНИЕ:
    SEND адрес сообщение /only /header header-obj
ОПИСАНИЕ:
     Отправить сообщение на адрес (или блок адресов)
     SEND - значение функции.
АРГУМЕНТЫ:
     адрес - адрес или блок адресов (Тип: блок электронной почты)
     сообщение - Текст сообщения. Первая строка - тема. (Тип: любой)
УТОЧНЕНИЯ:
     / only - отправить только одно сообщение на несколько адресов.
     / header - укажите свой собственный заголовок
         header-obj - заголовок для использования (Тип: объект)

Вся эта информация исходит из определения функции. Справку можно получить по всем типам функций, а не только по родным или встроенным функциям. Функция справки также может использоваться для пользовательских функций. Документация, которая отображается о функции, предоставляется при её определении.

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

Help "path"
Found these words:
     clean-path     (function)
     lit-path!      (datatype)
     lit-path?      (action)
     path!          (datatype)
     path-thru      (function)
     path?          (action)
     set-path!      (datatype)
     set-path?      (action)
     split-path     (function)
     to-lit-path    (function)
     to-path        (function)
     to-set-path    (function)

для отображения всех слов, содержащих path к строке.

Чтобы просмотреть список всех функций, доступных в REBOL, введите what в командной строке.

what
* [value1 value2]
** [number exponent]
+ [value1 value2]
- [value1 value2]
/ [value1 value2]
// [value1 value2]
< [value1 value2]
<= [value1 value2]
<> [value1 value2]
= [value1 value2]
== [value1 value2]
=? [value1 value2]
> [value1 value2]
>= [value1 value2]
? ['word]
?? ['name]
about []
abs [value]
absolute [value]
...

12. Просмотр исходного кода

Другой метод изучения REBOL и экономии времени при написании собственной функции - это посмотреть, сколько мезонинных функций REBOL определено. Для этого вы можете использовать функцию source.

source source
source: func [
    "Prints the source code for a word."
    'word [word!]
][
    prin join word ": "
    if not value? word [print "undefined" exit]
    either any [native? get word op? get word action? get word] [
        print ["native" mold third get word]
    ] [print mold get word]
]

Здесь функция source используется для печати собственного исходного кода.

Обратите внимание, что вы не можете увидеть исходный код для собственных функций, потому что они существуют только как машинный код. Однако исходная функция будет отображать спецификацию интерфейса собственной функции. Например:

source add
add: native [
    "Returns the result of adding two values."
    value1 [number! pair! char! money! date! time! tuple!]
    value2 [number! pair! char! money! date! time! tuple!]
]

Updated 8-Apr-2005 - Copyright REBOL Technologies - Formatted with MakeDoc2
REBOL.com Documents Manual Dictionary Library Feedback