Русский язык программирования Ади́на
1 Отличия от Racket
2 Основы языка
2.1 Простые значения
2.2 Выражения
2.3 Основы определений
2.4 Идентификаторы
2.5 Вызовы функций
2.6 Условные конструкции с если и операторами ?, && и ||
2.7 Вызовы функций, снова
2.8 Безымянные функции
2.9 Локальное связывание внутри функций и через выражение пусть
2.10 Списки, их перебор и рекурсия
2.10.1 Предопределённые циклы по спискам
2.10.2 Обход списка вручную
2.10.3 Хвостовая рекурсия
2.10.4 Рекурсия против цикла
2.11 Пары, списки и синтаксис Адины
2.11.1 Буквальный вывод пар и символов формой буквально
2.11.2 Сокращение буквально до апострофа
2.11.3 Списки и синтаксис Адины
3 Встроенные типы данных
3.1 Логические выражения
3.2 Числа
3.3 Литеры
3.4 Строки
3.5 Байты и байтовые строки
4 Справочник
4.1 Синтаксические формы
=
:  =
функция
буквально
пусто
пусто?
4.2 Логические выражения
булево?
==
===
/  =
4.3 Условия
?
&&
||
если
4.4 Символы
символ?
символ->строка
строка->символ
4.5 Числа
число?
точное?
неточное?
целое?
точное-целое?
целое-неотрицательное?
вещественное?
рациональное?
комплексное?
округлить
корень
синус
косинус
тангенс
арксинус
арккосинус
арктангенс
экспонента
логарифм
неточное->точное
точное->неточное
небольшое-число?
абс
4.6 Литеры
литера?
литера->число
число->литера
4.7 Списки
список?
пустой?
пустой-список
список
пара?
пара
:
первый
оставшиеся
длина
развернуть
элемент-списка
подсписок
подсписок===
отобрать
отобразить
отобразить/  и
отобразить/  или
свернуть
добавить
+  +
4.8 Строки
строка?
новая-строка
длина-строки
элемент-строки
установить-элемент-строки!
подстрока
добавить-строки
прописные
строчные
строки-равны?
строки-возрастают?
строки-не-убывают?
строки-убывают?
строки-не-возрастают?
строки-равны?/  без-регистра
строки-возрастают?/  без-регистра
строки-не-убывают?/  без-регистра
строки-убывают?/  без-регистра
строки-не-возрастают?/  без-регистра
прописные/  местные
строчные/  местные
строки-равны?/  местные
строки-возрастают?/  местные
строки-убывают?/  местные
строки-равны?/  местные/  без-регистра
строки-возрастают?/  местные/  без-регистра
строки-убывают?/  местные/  без-регистра
строка-начинается-с?
строка-заканчивается-на?
4.9 Байты
байт?
байты?
новые-байты
длина-байтов
элемент-байтов
установить-элемент-байтов!
байты->строка
байты->строка/  местные
4.10 Ввод-вывод
порт-вывода?
порт-ввода?
порт?
записать
вывести
прочитать-строку
текущий-порт-вывода
текущий-порт-ввода
текущее-место
4.11 Функции
функция?
4.12 Соответствия
соответствие?
создать-соответствие
4.13 Параметры
параметр?
параметр
параметризуя
4.14 Модули
используется
4.15 Операционная система
замерить-время
применить-замеряя-время
4.16 Приоритет операторов
8.12

Русский язык программирования Ади́на🔗

Клочков Роман <kalimehtar@mail.ru>

Документация основана на The Racket Guide.

 #lang 1 package: russian-lang

Это руководство описывает русскоязычный язык программирования, основанный на идеях из расширения синтаксиса Scheme readable.

Название Ади́на взято из названия симпатичного кустарника.

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

Для включения синтаксиса данного языка просто укажите в модуле Racket в первой строке

#lang 1

или

#!1

Второй вариант рекомендуется при использовании русского языка для написания программы.

1 Отличия от Racket🔗

Эта глава предназначена для тех, кто умеет программировать на Scheme и/или Racket. Остальные могут её пропустить и перейти к следующей.

На Адине можно писать как на Racket с упрощённым синтаксисом. Обратная совместимость поддерживается почти полностью, за исключением строчных комментариев и квадратных и фигурных скобок. Если в Racket использовалась «;», то здесь для строчных комментариев необходимо использовать «--», так как «;» используется в других синтаксических конструкциях, которые будут описаны ниже. Квадратные и фигурные скобки также нельзя использовать вместо круглых, так как они несут другой синтаксический смысл.

То есть, например, программа

#!1
(letrec ((is-even? (lambda (n)
                     (or (zero? n)
                         (is-odd? (sub1 n)))))
         (is-odd? (lambda (n)
                    (and (not (zero? n))
                         (is-even? (sub1 n))))))
  (is-odd? 11))

будет также, как и в Racket, возвращать #t. Но этот язык позволяет сделать списки, из которых состоит программа на Racket, читабельней. В нём структуру списка можно обозначить не скобками, а отступами. Список можно записать как
#!1
список 1 2 3 4 5 6
или как
#!1
список 1 2 3 4
   5
   6
Функция список синоним функции list.

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

Для иллюстрации запишу (список 1 2 (список 3 4) 5 (список 6 (список 7 8)))
#!1
список 1 2
  список 3 4
  5
  список 6
    список 7 8

Также есть специальная конструкция для списков, первым элементом которых тоже является список. В этом случае для дополнительного отступа можно использовать «;». Либо её же можно использовать для разделения списка на подсписки.

Например,
(let ((x 1) (y 2))
  (f x y))
можно записать как
#!1
let
  ;
    x 1
    y 2
  f x y
или как
#!1
let (x 1; y 2)
  f x y

Синтаксическое правило выглядит так: если в списке встречается «;», то список разделяется на подсписки, как если бы вместо «;» был перенос строки с сохранением отступа.

Таким образом, последовательности элементов «x 1» и «y 2» становятся вложенными списками.

Запишем предыдущий пример, описывая структуру программы отступами
#!1
letrec
  ;
    is-even?
      lambda (n)
        or
          zero? n
          is-odd?
            sub1 n
    is-odd?
      lambda (n)
        and
          not
            zero? n
          is-even?
            sub1 n
  is-odd? 11

Есть ещё одна синтаксическая конструкция, заимствованная из Haskell, позволяющая сократить количество строк не добавляя скобок. Литера «$» показывает, что элементы справа от него являются списком, который должен быть подставлен на место этой литеры.

(список 1 2 (список 3 4) 5 (список 6 (список 7 8))) теперь можно записать как

#!1
список 1 2
  список 3 4; 5
  список 6 $ список 7 8

А пример из Racket как
#!1
letrec
  ;
    is-even? $ lambda (n)
      or
        zero? n
        is-odd? $ sub1 n
    is-odd? $ lambda (n)
      and
        not $ zero? n
        is-even? $ sub1 n
  is-odd? 11

Таким образом получаем наглядное представление программы, которое не перегружено скобками.

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

Если перед скобкой нет пробела, то включается особый алгоритм обработки. Для круглой скобки элемент перед скобкой добавляется в голову списка. Элементы внутри спиcка можно (но не обязательно) разделять при помощи «;».

(список 1 2 (список 3 4) 5 (список 6 (список 7 8))) можно выразить как
#!1
список(1; 2; список 3 4; 5; список 6 $ список 7 8)

Так можно записывать в одну строку вызовы функций с аргументами, которые являются вызовами функций. Кроме того, таким образом удобно вызывать каррированные функции Вместо (((f 5) 6) 7) будет f(5)(6)(7).

Для квадратной скобки конструкция преобразуется в инструкция доступа к коллекции (массиву/списку/хэшу).

Вместо (vector-ref a 5) можно просто писать a[5]. А вместо (vector-ref (vector-ref a 5) 6)  a[5][6].

При помощи фигурных скобок есть возможность вызвать методы объекта.

(send window show #t) можно записать как window {show #t}. также можно использовать несколько вызовов как в send+.

(send* (new point%)
 (move-x 5)
 (move-y 7)
 (move-x 12))

преобразуется в
#!1
new(point%){move-x 5; move-y 7; move-x 11}
или
#!1
new(point%){move-x(5) move-y(7) move-x(11)}

Для удобства работы с арифметикой реализованы приоритеты бинарных операций. Если в списке обнаружена бинарная операция, то она становится в голову списка и получает элементы до и после неё как два аргумента-списка. Операцией считается любой индентификатор, который состоит только из !#$%&⋆+./<=>?@^~:*- и не равен .... Любой другой идентификатор можно сделать оператором добавив перед и после него литеры ^. Например, (2 ^cons^ 3) то же самое, что (cons 2 3).

Если надо, чтобы операция оставалась аргументом, то пишите её как список из точки и операции. Например, если надо написать (list + 3), то можно написать
#!1
list (. +) 3

Оператор равенства реализован как == (вместо equal?) и === (вместо eqv?), также реализованы // (как quotient), /= (неравно), ||, &&, % (как remainder).

(+ (vector-ref a 5) (* 2 (hash-ref h 'key))) можно написать как
#!1
a[5] + 2 * h['key]

Внимание: пробелы вокруг операций обязательны, так как 2*h, например, является нормальным именем переменной.

При помощи операций вышеупомянутый пример можно записать так:
#!1
letrec
  ;
    is-even? $ lambda (n)
      n === 0 || is-odd? (n - 1)
    is-odd? $ lambda (n)
      n /= 0 && is-even? (n - 1)
  is-odd? 11

2 Основы языка🔗

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

Комментарий начинается с литер --. и заканчивается концом строки.

Например, эта программа запрашивает имя и выводит приветствие:
#!1
вывести "введите имя: "
имя = прочитать-строку()
вывести
  "Привет, " ++ имя

2.1 Простые значения🔗

Значения языка программирования включают числа, логические значения, строки и массивы байтов. В DrRacket и документации они выделены зелёным цветом.

Числовые значения могут быть записаны как целые произвольной длины, в виде десятичных или простых дробей, с экспонентой или мнимой частью.
10      2.5
1/3     10200000000000.0
5+6i    12345678123456781234567812345678

Бесконечные целые и простые дроби позволяют выполнять арифметические операции без потери точности и риска переполнения. Числа с десятичной точкой или экспонентой являются вещественными числами двойной точности и хранят только 15-17 литер.

Логические значения — это истина и ложь. При проверках в логических операциях любое значение, не равное ложь трактуется как истина.

Строчные значения записываются между двойными кавычками. Для записи кавычек используется последовательность литер \", для записи литеры \ \\. Все остальные литеры Юникода можно писать как есть.

"Привет!"
"Автомобиль \"Москвич\""
"你好"

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

Примеры:
> 1.0000

1.0

> "\u0022ok\u0022"

"\"ok\""

2.2 Выражения🔗

Выражение — это команда языка Адина, которая возвращает значение.

Выражения записываются в виде последовательности слов, разделённых пробельными литерами.

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

Примеры:
> список 1 2 3

'(1 2 3)

> пара 5 6

'(5 . 6)

Если какие-то аргументы также являются функциями, то можно использовать отступы
> список 1 2 3 4
    список 5 6
    7

'(1 2 3 (5 6) 7)

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

Если по какой-либо причине выписывать последние элементы по одному на строке некрасиво, например, если первый аргумент является командой, а остальные простыми значениями, то можно функция писать в виде «функция(аргумент1 аргумент2 ...)». Предыдущий пример тогда будет выглядеть как
> список 1 2 3 4 список(5 6) 7

'(1 2 3 (5 6) 7)

Следует запомнить, что в таком случае скобка должна идти сразу за именем функции.

Ещё один альтернативный способ записи: в стиле лиспа. Можно просто взять всю команду в скобки:
> список 1 2 3 4 (список 5 6) 7

'(1 2 3 (5 6) 7)

и тогда внутри скобок переносы и отступы игнорируются.

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

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

Также есть ещё две особые синтаксических конструкции: «список 1 2 3 4 список(5 6)» можно записать как «список 1 2 3 4 $ список 5 6», то есть оператор $ позволяет слова после неё выделить в отдельную команду. Чтобы объединить несколько коротких команд или значений в одну строку, можно использовать оператор ;.

Пример:
> список 1 2 3 4
    список 5 6
    7; список 8; 9

'(1 2 3 4 (5 6) 7 (8) 9)

Можно заметить, что перед ; пробел не обязателен.

Операторы $ и ; работают также и в скобках, но ; разбивает выражение на подвыражения равного уровня, то есть

Пример:
> (список; список 1 2; список 3 4)

'((1 2) (3 4))

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

Пример:
> ;
    список
    список 1 2
    список 3 4

'((1 2) (3 4))

Также внутри скобок можно использовать функциональный синтаксис со скобкой сразу после имени функции.

Пример:
> (список; список(1 2); список 3 4; список())

'((1 2) (3 4) ())

Некоторые слова являются операторами. Оператором является слово, состоящее только из литер !#$%&⋆+./<=>?@^~:*-, которые называются операторными литерами. Исключения: слова . и ... операторами не являются. Также оператором является любое слово, которое начинается и заканчивается на ^. Примеры операторов: +, -, ^пара^.

Если оператор встречается в команде и не является первым словом, то из выражений до оператора будет собрано одно выражение, а из выражений после — второе. Затем будет сформирована команда, в которой первым словом будет оператор, а его аргументами — эти два выражения. Например (список 2 3 ++ список 4 5 6) то же самое, что (++ (список 2 3) (список 4 5 6)). Особым образом обрабатываются операторы = и ?: выражения после них не объединяются в одно, а переносятся в результирующую команду как есть, потому что эти операторы требуют больше двух аргументов.

Если оператор начинается и заканчивается на ^ и между ними есть литеры кроме операторных, то он вызывает функцию по имени между ^ со своими аргументами. Например, (2 ^пара^ 3) то же самое, что (пара 2 3). Таким образом можно любую двухаргументную функцию использовать как оператор.

Если в строке несколько операторов, то порядок их применения определяется приоритетами. Например, (2 + 2 * 2) будет равно 6, как и должно с точки зрения приоритетов арифметических операторов: на первом шаге преобразуется в (+ 2 (2 * 2)), затем в (+ 2 (* 2 2)),и затем вычислится как (+ 2 4) = 6.

2.3 Основы определений🔗

При описании синтаксиса «...» обозначает, что предыдущий элемент может повторяться 0 и более раз, «...+» — 1 и более раз. В угловых скобках указываются синтаксические переменные. Например, вместо <идентификатор> может быть подставлен любой допустимый имдентификатор языка.

Определение в форме

(<идентификатор> = <выражение>)

cвязывает <идентификатор> с результатом вычисления выражения, а в форме

(<идентификатор>(<идентификатор> ...) = <команда> ... <выражение>)

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

Команда внутри функции может также являться определением. В этом случае связывание видно только внутри функции.

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

Примеры:
> часть = 3
> кусок строка =
    подстрока строка 0 часть
> часть

3

> кусок "три литеры"

"три"

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

Примеры:
> испечь вкус =
    вывести "разогрев печи...\n"
    вкус ++ " пирог"
> испечь "вишнёвый"
разогрев печи...

"вишнёвый пирог"

Если попробовать записать функцию в одну строку, то получится

Примеры:
> не-печётся вкус = вкус ++ " пирог"
> не-печётся "вишнёвый"

" пирог"

Это потому, что определение прочитано как
не-печётся вкус =
  вкус
  ++
  " пирог"
и последовательно выполняется: вычисление значения переменной, значения операции и строки. Последнее возвращается как результат функции.

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

Примеры:
> кусок

#<функция:кусок>

> подстрока

#<функция:подстрока>

2.4 Идентификаторы🔗

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

Примеры идентификаторов:
не-печётся
++
=
Проверка
проверка/вывод
а+б
с+1
1\;2\\3
|идентификатор со спецлитерами ( ) [ ] { } " , ' ` ; # \|

2.5 Вызовы функций🔗

Мы уже видели много вызовов функций. Синтаксис вызова
(<имя> <выражение> ...)
где количество выражений определяется количеством аргументов функции с именем <имя>.

Разумеется, при записи с начала строки скобки можно опустить.

Язык Адина предопределяет множество функций, таких как подстрока и добавить-строки. Ниже будут ещё примеры.

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

> добавить-строки "рос" "сель" "торг" -- добавить строки

"россельторг"

> подстрока "паровоз" 0 3 -- извлечь подстроку

"пар"

> строка? "это строка" -- распознать строку

истина

> строка? 42

ложь

> корень 16 -- вычислить квадратный корень

4

> корень -16

0+4i

> + 1 2 -- сложить

3

> - 2 2 -- вычесть

1

> < 2 2 -- сравнить

ложь

> >= 2 2

истина

> число? "это не число" -- распознать число

ложь

> число? 1

истина

> == 6 "шесть" -- сравнить что угодно

ложь

> == 6 6

истина

> == "шесть" "шесть"

истина

Если функция является оператором, то её можно писать вторым словом.

Примеры:
> 1 + 2 -- сложить

3

> 2 - 1 -- вычесть

1

> 2 < 1 -- сравнить

ложь

> 2 >= 1

истина

> 6 == "шесть" -- сравнить что угодно

ложь

> 6 == 6

истина

> "шесть" == "шесть"

истина

2.6 Условные конструкции с если и операторами ?, && и ||🔗

Следующий простейший вид выражения — это условное выражение:
(? <выражение-условия> <выражение-если-истина> <выражение-если-ложь>)

Первое выражение вычисляется всегда. Если его результат равен ложь, тогда условное выражение вычисляет выражение-если-ложь и возвращает его резулоьтат. Если же результат любой другой, то вычисляется и возвращается выражение-если-истина. Обратите внимание, что оба выражения (на истину и ложь) обязательны.

Оператор ? имеет три аргумента и поэтому обрабатывается особым образом. Слова слева от него объединяются в одно выражение, а слова справа остаются как есть, также как в определении функции.

Пример:
> 2 > 3 ?
    "2 больше, чем 3"
    "2 не больше, чем 3"

"2 не больше, чем 3"

Для следующих примеров необходимо ввести команду
> используется строка
чтобы была возможность использовать функции строка-начинается-с? и строка-заканчивается-на?.

ответ запрос =
  строка-начинается-с? запрос "Привет" ? "Привет!" "Чего?"
> ответ "Приветствую, Адина!"

"Привет!"

> ответ "λx:(μα.α→α).xx"

"Чего?"

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

(если <выражение-условия> ... тогда <выражения-если-истина> ... иначе <выражения-если-ложь> ...)
(если <выражение-условия> ... тогда <выражения-если-истина> ...)

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

Пример:
ответ запрос =
  если строка-начинается-с? запрос "Привет" тогда
    "Привет!"
    иначе
    "Чего?"

Обратите внимание, что «иначе» не имеет отступа относительно команд до и после него, как встречается в других языках программирования.

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

ответ-на-что-угодно запрос =
  строка? запрос ?
    строка-начинается-с? запрос "Привет" ?
      "Привет!"
      "Чего?"
    "Чего?"

Вместо того, чтобы дублировать ветку «Чего?», лучше записать эту функцию как:

ответ-на-что-угодно запрос =
  ?
    строка? запрос ?
      строка-начинается-с? запрос "Привет" 
      ложь
    "Привет!"
    "Чего?"

Но такие вложенные условия сложно читать. Адина предоставляет удобочитаемые короткие формы:

(&& <выражение>*)
(|| <выражение>*)

Форма && выполняет выражения-аргументы. Если текущее выражение возвращает ложь, то остальные выражения не вычисляются. Возвращается результат последнего вычисленного выражения. Такой способ вычисления называется вычисления по короткой схеме.

Форма || аналогично выполняет выражения пока они возвращают ложь.

Также обратите внимание, что эти обе формы являются операторами, поэтому если выражений всего два, то можно ставить оператор между выражениями. Приоритет && выше, чем приоритет ||.

Примеры:
ответ-на-что-угодно запрос =
  строка? запрос && строка-начинается-с? запрос "Привет" ?
    "Привет!"
    "Чего?"
> ответ-на-что-угодно "Приветствую, Адина!"

"Привет!"

> ответ-на-что-угодно 17

"Чего?"

Обратите внимание, что здесь в одном выражении есть операторы && и ?. Так как приоритет оператора && выше, то сначала группируется всё выражение слева от ?, а потом уже результат сравнения используется как условие.

Обратите внимание, что && и || работают с любым количеством выражений:

Примеры:
ответ-на-восклицание запрос =
  ?
    &&
      строка? запрос
      строка-начинается-с? запрос "Привет"
      строка-заканчивается-на? запрос "!"
    "Привет!"
    "Чего?"
> ответ-на-восклицание "Приветствую, Адина!"

"Привет!"

> ответ-на-восклицание "Приветствую."

"Чего?"

То же самое можно сделать в операторном стиле:

ответ-на-восклицание запрос =
  строка? запрос && строка-начинается-с? запрос "Привет" \
                 && строка-заканчивается-на? запрос "!" ?
    "Привет!"
    "Чего?"

Как видно, пришлось сделать перенос строки, чтобы она была не слишком длинной. Выбирайте ту синтаксическую конструкцию, которую потом будет легче читать.

Часто вложенные условия используются для проверки последовательности условий, каждое из которых возвращает свой результат:

больше-ответов запрос =
  строка-начинается-с? запрос "Привет" ?
    "Привет!"
    строка-начинается-с? запрос "Пока" ?
      "Пока!"
      строка-заканчивается-на? запрос "?" ?
        "Я не знаю."
        "Чего?"

Короткая форма записи для последовательности проверок если без «тогда».

(если (<выражение> <команда> ... <выражение>) ...)

В этом варианте синтаксиса тело формы если состоит из последовательности правил. Каждое правило состоит из выражения и последовательности команд. Если выражение истинно (не равно ложь), то выполняется последовательность команд и возвращается результат последнего выражения. Если ложно, то аналогично обрабатывается следующее правило. В последнем правиле можно писать «иначе» вместо истина. Если команды вводят определения, то они видны только внутри правила.

Таким образом можно переписать функцию больше-ответов как:

больше-ответов запрос =
  если
    строка-начинается-с?(запрос "Привет") "Привет!"
    строка-начинается-с?(запрос "Пока") "Пока!"
    строка-заканчивается-на?(запрос "?") "Я не знаю."
    иначе "Чего?"
> больше-ответов "Приветствую!"

"Привет!"

> больше-ответов "Пока, Адина."

"Пока!"

> ответ-на-восклицание "Какой твой любимый цвет?"

"Я не знаю."

> больше-ответов "Мой зелёный."

"Чего?"

Обратите внимание, что условное выражение обязательно должно быть одним элементом. То есть оно либо должно быть одним словом, либо вызовом функции со скобками как в этом примере, либо просто взято в скобки. Кроме того, обязателен отступ после каждого условия или «иначе».

Если условное выражение очень сложное, то можно использовать синтаксис с «;» в качестве начала правила:

Пример:
ответ-на-восклицание запрос =
  если
    ;
      &&
        строка? запрос
        строка-начинается-с? запрос "Привет"
        строка-заканчивается-на? запрос "!"
      "Привет!"
    иначе
      "Чего?"

2.7 Вызовы функций, снова🔗

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

(<выражение> <выражение> ...)

Первое выражение может быть идентификатором переменной, содержащей функции, такой как добавить-строки или +. Но может быть и любым другим выражением, результатом которого является функция. Например, это может быть условное выражение:
удвоить п =
  (строка? п ? добавить-строки +) п п
> удвоить "бла"

"блабла"

> удвоить 5

10

Если выражение, вычисляющее функцию достаточно сложно, то можно использовать синтаксис с «;» в начале вызова:
удвоить п =
  ;
    строка? п ? добавить-строки +
    п; п

Также не забывайте, что если используете оператор в качестве значения и он не на первом и не на последнем месте в выражении, то его надо писать в виде (. +).

Синтаксически, первый элемент списка может быть любым значением, но при выполнеии будет ошибка:

> 1 2 3 4
вызов функции: ожидалась функция, которую можно применить к аргументам получено: 1

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

2.8 Безымянные функции🔗

Программирование было бы утомительным, если приходилось именовать все значения. Вместо того, чтобы написать 1 + 2, пришлось бы писать:
> а = 1
> б = 2
> а + б

3

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

2

Здесь ф $ ф п является бесскобочным вариантом ф (ф п). Если скобка заканчивается с концом команды, то вместо открывающей скобки можно поставить разделитель «$» и убрать закрывающую.

Если в функцию дважды надо передать ещё не определённую функцию, то её придётся сначала определить, а потом передать в дважды.

> громче строка =
    строка ++ "!"
> дважды громче "Привет"

"Привет!!"

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

(функция (<идентификатор> ...) <команда> ... <выражение>)

Вызов этой формы возвращает новую функцию:
> функция (строка)
    строка ++ "!"

#<функция>

Так вышеприведённый пример может быть переписан как:
> дважды
    функция (строка) $ строка ++ "!"
    "Привет"

"Привет!!"

> дважды
    функция (строка) $ строка ++ "?!"
    "Привет"

"Привет?!?!"

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

> добавлятель-суффикса строка2 =
    функция (строка) $ строка ++ строка2
> дважды добавлятель-суффикса("!") "Привет"

"Привет!!"

> дважды добавлятель-суффикса("?!") "Привет"

"Привет?!?!"

> дважды добавлятель-суффикса("...") "Привет"

"Привет......"

Адина — язык с лексической областью видимости. Это значит, что строка2 в функции, возвращённой из вызова добавлятель-суффикса, всегда ссылается на аргумент вызова, который создал функцию. Другими словами, функция, полученная выражением функция, помнит правильное значение строка2.

> громче = добавлятель-суффикса "!"
> неувереннее = добавлятель-суффикса "?"
> дважды неувереннее "действительно"

"действительно??"

> дважды громче "действительно"

"действительно!!"

Когда используется определение в форме <идентификатор> = <выражение>, то также можно определить функцию. Эти два определения эквивалентны:

> громче строка =
    строка ++ "!"
> громче = функция (строка)
    строка ++ "!"
> громче

#<функция:громче>

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

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

> добавлятель-суффикса(строка2)(строка) = $ строка ++ строка2

2.9 Локальное связывание внутри функций и через выражение пусть🔗

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

> преобразовать строка =
    начинается? строка2 = -- видно только в функции «преобразовать»
      строка2-с-пробелом = строка2 ++ " " -- видно только в функции «начинается?»
      строка-начинается-с? строка строка2-с-пробелом
    если
      начинается?("Привет") "Привет!"
      начинается?("Пока") "Пока!"
      иначе "Чего?"
> преобразовать "Привет, мир!"

"Привет!"

> преобразовать "Приветствую, Земля!"

"Чего?"

> преобразовать "Пока, друзья."

"Пока!"

> преобразовать "Гы"

"Чего?"

> начинается? -- вне функции «преобразовать», поэтому ...
начинается?: не определено; не могу использовать идентификатор до его определения

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

> добавлятель-суффикса строка2 =
    результат строка =
      строка ++ строка2
    результат

Ещё один способ сделать ограниченную привязку — использование выражения пусть.

(пусть ((<имя> <выражение>) ...) <команда> ... <выражение>)

Внутри этой формы после заголовка с парами имён и выражений значения выражений связываются с соответсвующими им именами. Форму пусть можно использовать, если нужно ввести имена внутри выражения или если выражения в заголовке ссылаются на такие же имена вне формы пусть.

> а = 5
> пусть (а (а + 2); б (а - 1))
    список а б

'(7 4)

В выражениях (а + 2) и (а - 1) используется значение «а», установленное перед формой пусть.

2.10 Списки, их перебор и рекурсия🔗

Адина семантически является диалектом языка Лисп. Поэтому в ней есть мощные встроенные средства работы со списками.

Функция список получает любое количество значений и возвращает список из этих значений.

> список "красный" "зелёный" "синий"

'("красный" "зелёный" "синий")

> список 1 2 3 4 5

'(1 2 3 4 5)

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

Для работы со списками есть множество функций и несколько операторов. Вот несколько примеров:
> длина $ список "раз" "два" "три" -- считаем элементы

3

> элемент-списка список("раз" "два" "три") 1 -- получаем элемент по номеру позиции

"два"

> список("раз" "два" "три")[0] -- то же самое оператором

"раз"

> добавить список("раз" "два") список("три") -- объединяем элементы

'("раз" "два" "три")

> список "раз" "два" ++ список "три" -- то же самое оператором

'("раз" "два" "три")

> подсписок "четыре" $ список "раз" "два" "три" -- проверяем наоичие элемента

ложь

2.10.1 Предопределённые циклы по спискам🔗

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

Разные функции обработки элементов по разному комбинируют результаты обработки. Функция отобразить собирает результаты выполнения обработки в новый список То есть математически отображает обрабатываемый список на новый список.

> отобразить корень $ список 1 4 9 16

'(1 2 3 4)

> отобразить
    функция (с) $ с ++ "!"
    список "орехи" "печенье" "шоколад"

'("орехи!" "печенье!" "шоколад!")

Функции отобразить/и и отобразить/или объединяют результаты через && и || соответственно.

> отобразить/и строка? $ список "а" "б" "в"

истина

> отобразить/и строка? $ список "а" "б" 6

ложь

> отобразить/или число? $ список "а" "б" "в"

истина

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

> отобразить
    функция (с ч) $ подстрока с 0 ч
    список "орехи" "печенье" "шоколад"
    список 4 6 3

'("орех" "печень" "шок")

Функция отобрать оставляет только те элементы, для которых результат функции не равен ложь.

> отобрать строка? $ список "а" "б" 6

'("а" "б")

> отобрать
    функция (ч) $ ч > 0
    список 1 -2 6 7 0

'(1 6 7)

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

> свернуть
    функция (элемент текущее)
      текущее + элемент * элемент
    0
    '(1 2 3)

14

2.10.2 Обход списка вручную🔗

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

Так как в Адине список является односвязным списком, то базовыми операциями для непустого списка являются:

Примеры:

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

Примеры:
> пустой-список

'()

> пара "голова" пустой-список

'("голова")

> пара "белая" $ пара "голова" пустой-список

'("белая" "голова")

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

Примеры:
> "голова" : .

'("голова")

> "белая" : "голова" : .

'("белая" "голова")

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

Примеры:

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

Примеры:
моя-длина сп =
  пустой? сп ?
    0
    1 + моя-длина $ оставшиеся сп
> моя-длина пустой-список

0

> моя-длина $ список "а" "б" "в"

3

моё-отобразить ф сп =
  пустой? сп ?
    пустой-список
    ф первый(сп) : моё-отобразить ф $ оставшиеся сп
> моё-отобразить прописные $ список "на старт" "внимание" "марш"

'("НА СТАРТ" "ВНИМАНИЕ" "МАРШ")

Алгоритмы для списочных структур удобно описывать через рекурсию как в вышеприведённых примерах. При реализации алгоритма для списка длины Н предполагаем, что для списка длины Н-1 реализация уже есть и описываем всего два варианта результата: значение для Н = 0 и вычисление для всех остальных, используя описываемую функцию для хвоста списка.

2.10.3 Хвостовая рекурсия🔗

И моя-длина и моё-отобразить при работе требуют место для хранения временных значений пропорционально длине обрабатываемого списка. Иначе говоря, использумая память О(n).

Это легко увидеть, если представить, как моя-длина $ список "а" "б" "в" должна вычисляться:
моя-длина $ список "а" "б" "в"
= 1 + моя-длина $ список "б" "в"
= 1 + (1 + моя-длина $ список "в")
= 1 + (1 + (1 + моя-длина пустой-список))
= 1 + (1 + (1 + 0))
= 1 + (1 + 1)
= 1 + 2
= 3

Для списка из n элементов вычисление будет запоминать операции сложения n раз и выполнять их только когда список закончится.

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

моя-длина сп =
  -- локальная функция цикл
  цикл сп н
    пустой? сп ?
      н
      цикл
        оставшиеся сп
        1 + н
  -- тело функции моя-длина - вызов функции цикл
  цикл сп 0

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

моя-длина $ список "а" "б" "в"
= цикл список("а" "б" "в") 0
= цикл список("б" "в") 1
= цикл список("в") 2
= цикл пустой-список 3
= 3

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

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

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

2.10.4 Рекурсия против цикла🔗

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

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

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

удалить-повторы сп =
  пустой? сп || пустой? оставшиеся(сп) ?
    сп
    первый сп == первый оставшиеся(сп) ?
      удалить-повторы $ оставшиеся сп
      первый сп : удалить-повторы $ оставшиеся сп
> удалить-повторы $ список "а" "б" "б" "б" "в" "в"

'("а" "б" "в")

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

удалить-повторы $ список "а" "б" "б" "б" "б" "б"
= "а" : удалить-повторы $ список "б" "б" "б" "б" "б"
= "а" : удалить-повторы $ список "б" "б" "б" "б"
= "а" : удалить-повторы $ список "б" "б" "б"
= "а" : удалить-повторы $ список "б" "б"
= "а" : удалить-повторы $ список "б"
= "а" : список "б"
= список "а" "б"

2.11 Пары, списки и синтаксис Адины🔗

Функция пара (и соответсвующая операция «:») на самом деле принимает любые два аргумента, а не только список в качестве второго аргумента. Если второй аргумент на создан при помощи этой функции и не является пустым списком, то результат выводится особым образом. Два значения, объединённые при помощи функции пара выводятся в скобках как список, но с точкой между ними.

> пара 1 2

'(1 . 2)

> пара "мир" "дверь"

'("мир" . "дверь")

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

Примеры:
> первый $ пара 1 2

1

> оставшиеся $ 1 : 2

2

> пара? пустой-список

ложь

> пара? $ 1 : 2

истина

> пара? $ список 1 2 3

истина

Наверное, чаще всего такие пары встречаются, когда при построении списка программист путает аргументы местами:

> пара список(2 3) 1

'((2 3) . 1)

> пара 1 список(2 3)

'(1 2 3)

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

Если второй элемент пара, но не список, то результат выводится так:
> 0 : 1 : 2

'(0 1 . 2)

В целом, можно считать, что запись через точку используется всегда, но если после точки идёт пара, то тогда убирается точка и та пара сразу пишется через пробел. Таким образом, '(0 1 . 2) сокращается до '(0 1 . 2), а '(1 2 3) сокращается до '(1 2 3).

2.11.1 Буквальный вывод пар и символов формой буквально🔗

Списки выводятся с апострофом перед ними, но если элемент списка тоже список, то апострофа перед ним нет.

> список список(1) список(2 3) список(4)

'((1) (2 3) (4))

Форма буквально позволяет писать списки таким же образом

> буквально "красный" "зелёный" "синий"

'("красный" "зелёный" "синий")

> буквально (1) (2 3) (4)

'((1) (2 3) (4))

> буквально ()

'()

Эта форма также позволяет писать через точку:

> буквально 1 . 2

'(1 . 2)

> буквально 0 1 . 2

'(0 1 . 2)

Разумеется, можно вкладывать списки любого вида:
> список список(1 2 3) 5 список("a" "b" "c")

'((1 2 3) 5 ("a" "b" "c"))

> буквально (1 2 3) 5 ("a" "b" "c")

'((1 2 3) 5 ("a" "b" "c"))

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

> буквально иван-иванович

'иван-иванович

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

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

Фактически, символ хранит только строку со своим именем. В этом смысле символы и строки отличаются только тем, как они выводятся. Функции символ->строка и строка->символ преобразуют их друг в друга.

Примеры:

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

> первый $ буквально (имя фамилия)

'имя

> символ? $ первый $ буквально (имя фамилия)

истина

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

> буквально (имя фамилия)

'(имя фамилия)

Форма буквально не оказывает влияния на уже буквальные выражения, например, числа и строки:
> буквально 42

42

> буквально "для записи"

"для записи"

2.11.2 Сокращение буквально до апострофа🔗

Как уже возможно стало понятно, можно сократить форму буквально, просто ставя вместо неё апостроф.

> '(1 2 3)

'(1 2 3)

> ' 1 2 3

'(1 2 3)

> 'имя

'имя

> '((1 2 3) имя ("а" "б" "в"))

'((1 2 3) имя ("а" "б" "в"))

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

Апостроф преобразуется в буквально простой подстановкой:
> первый ''имя

'буквально

> первый '(буквально имя)

'буквально

При выводе аналогично. Если печататель видит символ 'буквально как первый элемент двухэлементного списка, то он вместо этого печатает апостроф:

> буквально буквально имя

'(буквально имя)

> '(буквально имя)

'(буквально имя)

> ''имя

'(буквально имя)

2.11.3 Списки и синтаксис Адины🔗

Синтаксис Адины не определяется напрямуй в терминах потоков литер. Вместо этого синтаксис определяется двумя слоями:

Правила для чтения списков и выражений одинаковы. В частности, это позволяет использовать запись через точку при записи выражений:
> + 1 . (2)

3

Это работает, так как «+ 1 . (2)» всего лишь другой метод для записи «+ 1 . 2».

Операторы также обрабатываются на стадии чтения с учётом приоритетов. Результат чтения можно увидеть при помощи буквально.

> буквально 2 + 2 * 2

'(+ 2 (* 2 2))

Это можно использовать, если есть сомнения в приоритете операторов или понимании сложной конструкции.

3 Встроенные типы данных🔗

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

3.1 Логические выражения🔗

В Адине есть две константы для представления логических (булевых) значений: истина и ложь. Функция булево? распознаёт эти две константы. Но при использовании в если, ?, &&, ||, ... любое значение кроме ложь трактуется как истинное.

Примеры:
> 1 + 1 == 2

истина

> булево? истина

истина

> булево? ложь

истина

> булево? "нет"

ложь

> "нет" ? 1 0

1

3.2 Числа🔗

Числа в Адине бывают точные и неточные.

Неточные числа выводятся с десятичной точкой или показателем экспоненты, а точные числа выводятся как целые числа или простые дроби. Такое же соглашение используется при чтении, но если необходимо, перед числом можно написать (латинскую) #e, тогда число будет прочитано как точное или #i тогда оно будет прочитано как неточное. Префиксы (латинскими) #b, #o и #x позволяют вводить числа в двоичной, восьмеричной и шестнадцатеричной системах счисления.

Примеры:
> 0.5

0.5

> #e0.5

1/2

> #x03BB

955

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

Примеры:
> 0.5

0.5

> 1 / 2

1/2

> 1 / 2.0

0.5

> 3.0 == 2.999 ? 1 2

2

> неточное->точное 0.1

3602879701896397/36028797018963968

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

Примеры:
> синус 0    -- рациональный результат

0

> синус 1/2  -- иррациональный результат

0.479425538604203

Вычисления с небольшими целыми числами производятся быстрее. Под небольшими подразумеваются числа, занимающие на несколько бит меньше, чем машинное представление знаковых чисел. Например, для 64-битной системы таковыми являются числа -1152921504606846976..1152921504606846975, но конкретные границы могут зависеть от используемой платформы. Для конкретного компьютера можно проверять при помощи функции небольшое-число?.

Вычисления с большими целыми числами или с точными нецелыми числами медленнее, чем вычисления с неточными числами.

сумма ф а б =
  а == б ? 0
    ф а + сумма ф (а + 1) б
> замерить-время $ округлить $ сумма (функция (ч) $ 1.0 / ч) 1 2000
время процессора: 24 реальное: 12 сборки мусора: 0
> замерить-время $ округлить $ сумма (функция (ч) $ 1 / ч) 1 2000
время процессора: 0 реальное: 0 сборки мусора: 0

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

Примеры:
> целое? 5

истина

> комплексное? 5

истина

> целое? 5.0

истина

> целое? 1+2i

ложь

> комплексное? 1+2i

истина

> комплексное? 1.0+2.0i

истина

> абс -5

5

> абс -5+2i
абс: нарушение контракта ожидалось: вещественное? получено: -5+2i
> синус -5+2i

3.6076607742131563+1.0288031496599335i

Операторы === и == сравнивают числа с учётом того, точное ли число

Примеры:
> 1 === 1.0

ложь

> 1 == 1.0

ложь

> 1 >= 1.0 && 1 <= 1.0

истина

Сравнение неточных чисел может приводить к неожиданным результатам. Даже достаточно простые неточные числа могут обозначать не то, что можно было бы подумать. Например, формат IEEE, будучи основанным на степенях двойки, может представить 1/2 точно, но 1/10 только приближённо.

Примеры:
> 1/2 >= 0.5 && 0.5 <= 1/2

истина

> 1/10 >= 0.1

ложь

> неточное->точное 0.1

3602879701896397/36028797018963968

3.3 Литеры🔗

Литеры Адины соответствуют кодам Юникода. Код Юникола можно трактовать как беззнаковое целое число, которое можно отобразить в 21 бит и которое соответствует символу естественного языка или части символа. Технически, код является более простым понятием, чем то, что в стандарте Юникода называется символом, но его достаточно в большинстве случаев. Например, любую акцентированную латинскую букву, любую кириллическую букву или любой обычный китайский иероглиф можно представить в виде кода.

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

Печатные литеры обычно выводятся как #\ и отображаемая литера. Непечатные обычно выводятся как #\u и код литеры в виде шестнадцатиричного числа. Некоторые литеры печатаются особым образом: например, пробел и перенос строки выводятся как #\пробел и #\перенос.

Примеры:
> число->литера 1025

#\Ё

> литера->число #\Ё

1025

> #\λ

#\λ

> число->литера 17

#\u0011

> литера->число #\пробел

32

Функция вывести прямо пишет переданную литеру в текущий порт вывода, а не использует синтаксис для вывода литерных констант.

Примеры:
> #\Ё

#\Ё

> вывести #\Ё

"Ё"

3.4 Строки🔗

Строка — это массив литер фиксированной длины. Она выводится при помощи двойных кавычек (знаков дюйма). Если в строке встречается двойная кавычка, она выводится как \", если встречается обратная косая черта, то \\. Также, при помощи обратной косой черты выводятся пробельные литеры: \n перенос строки, \r возврат каретки. Большинство непечатных литер выводится как \u и четырёхзначный шестнадцатеричный номер литеры.

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

Примеры:
> "Пример"

"Пример"

> "\u03BB"

"λ"

> вывести "Пример"
Пример
> вывести "Пример с \"кавычками\""
Пример с "кавычками"
> вывести "две\nстроки"
две строки
> вывести "\u03BB"
λ

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

Примеры:
> элемент-строки "Эльбрус" 0

#\Э

> "Эльбрус"[0]

#\Э

> с = новая-строка 5 #\.
> с

"....."

> установить-элемент-строки! с 2 #\λ
> с

"..λ.."

> с[0] := #\ё

#\ё

> с

"ё.λ.."

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

Например, в Unicode «Ё» раньше, чем «Б», а в русском алфавите, наоборот:

Примеры:
> строки-возрастают? "Ёж" "Белка"

истина

> строки-возрастают?/местные "Ёж" "Белка"

ложь

> параметризуя
  $ текущее-место ложь
  строки-возрастают?/местные "Ёж" "Белка"

истина

Для работы с представлением строки в виде байтов нужно использовать байтовые строки.

3.5 Байты и байтовые строки🔗

Байт — это точное целое число с 0 по 255. Предикат байт? позволяет определить, является ли значение байтом.

Примеры:
> байт? 0

истина

> байт? 256

ложь

Байтовая строка — это массив байтов фиксированной длины. Работа с ней аналогична работе со строкой, но вместо литер в байтовой строке хранятся байты. При выводе байтовой строки байты от 32 по 126 выводятся как литеры с этими номерами, но перед кавычкой и обратной чертой как ипри выводе строк выводится обратная черта. Байты с 7 по 13 и 33 выводятся по их традиционным именам: #\a\b\t\n\v\f\r\e. Остальные выводятся в виде обратной черты и числа в восьмеричной кодировке.

Примеры:
> #"Elbrus"

#"Elbrus"

> элемент-байтов #"Elbrus" 0

69

> #"Elbrus"[0]

69

> новые-байты 3 65

#"AAA"

> а = новые-байты 2
> а

#"\0\0"

> установить-элемент-байтов! а 0 1
> а[1] := #o377

255

> а

#"\1\377"

Функция вывести выводит байтовую строку как поток байтов в порт вывода. Технически, вывод обычной (литерной) строки сводится к переводу строки в байты в кодировке UTF-8 и выводу полученных байтов в порт вывода, так как операционная система понимает вывод только в байтах.

Примеры:
> вывести #"Elbrus"
Elbrus
> вывести #"\316\273"  -- λ в кодировке UTF-8
λ

Для явного преобразования между строками и байтами Адина поддерживает UTF-8 и местную кодировку операционной системы. Также есть функции для преобразования между произвольными кодировками.

Примеры:
> байты->строка #"\316\273"

"λ"

> параметризуя
  $ текущее-место "C"
  байты->строка/местные #"\316\273"-- кодировка C понимает только байты от 0 по 127
байты->строка/местные: строка байтов не является правильной в местной кодировке строка байтов: #"\316\273"

4 Справочник🔗

4.1 Синтаксические формы🔗

синтаксис

(идентификатор = выражение)

(значения идентификатор ... = выражение)
(заголовок(аргументы) = команда ... выражение)
 
заголовок = идентификатор
  | (заголовок аргументы)
     
аргументы = аргумент ...
  | аргумент ... . аргумент-оставшихся
     
аргументы = идентификатор
  | [идентификатор выражение]
  | ключ идентификатор
  | [ключ идентификатор выражение]
Эта команда определяет новые переменные. Первая форма связывает идентификатор с результатом вычисления выражжения. Вторая позволяет одновременно связать несколько идентификаторов с значениями (выражение должно в этом случае возвращать необходимое количество значений). Третья связывает идентификатор с функцией, здесь особым образом обрабатывается оператор: каждый элемент после = считается отдельной командой. То есть, если надо сделать функцию из одного выражения, выражение должно быть одним элементом или обязательно после = делать перенос и отступ. Если заголовок является списком, то создаётся функция, возвращающая функцию с аргументами, указанными после первого элемента заголовка.

синтаксис

(идентификатор := выражение)

(значения идентификатор ... := выражение)
(выражение-коллекция[индекс] := выражение)
(доступ-к-полю выражение-структура := выражение)
Эта команда позволяет изменить значение существующей переменной. Первые две формы аналогичны первым двум формам команды = и позволяют изменить значение определённых ранее переменных. Третья форма позволяет при помощи квадратных скобок изменить значение элемента изменяемой коллекции: массива, строки, соответствия или списка. Учитывайте, что для списка время доступа пропорционально номеру элемента. Четвёртая форма позволяет изменить значение изменяемого поля структуры. Результатом этой команды является значение выражения.

синтаксис

(функция (аргументы) = команда ... выражение)

 
аргументы = аргумент ...
  | аргумент ... . аргумент-оставшихся
     
аргумент = идентификатор
  | [идентификатор выражение]
  | ключ идентификатор
  | [ключ идентификатор выражение]
Возвращает функцию с указанными аргументами и телом.

синтаксис

(буквально элемент ...+)

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

значение

пусто : пусто?

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

функция

(пусто? аргумент)  булево?

  аргумент : любой
Возвращает истина, если аргумент равен пусто

4.2 Логические выражения🔗

функция

(булево? аргумент)  булево?

  аргумент : любой
Возвращает истина, если аргумент истина или ложь, в противном случае возвращает ложь.

функция

(== аргумент ...+)  булево?

  аргумент : любой
Возвращает истина, если аргументы равны. Списки и массивы считаются равными, если равны их элементы.

функция

(=== аргумент ...+)  булево?

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

функция

(/= аргумент ...+)  булево?

  аргумент : любой
Возвращает ложь, если аргументы равны в смысле ==.

4.3 Условия🔗

синтаксис

(? условие выражение-если-истина выражение-если-ложь)

 
  условие : булево?
Если условие истинно, выполняет выражение-если-истина иначе выполняет выражение-если-ложь. Возвращает результат выполненного выражения.

При использовании как оператор не объединяет в одно выражение слова справа от себя.

синтаксис

(&& выражение ...)

Выполняет выражения слева направо, пока одно из них не вернёт ложь или они не закончатся. Возвращает результат последнего выполненного выражения.

синтаксис

(|| выражение ...)

Выполняет выражения слева направо, пока одно из них не вернёт что-то кроме ложь или они не закончатся. Возвращает результат последнего выполненного выражения.

синтаксис

(если слова-условия ... тогда команда ... иначе команда ...)

(если слова-условия ... тогда команда ...)
(если правило ...)
(если правило ... (иначе команда ... выражение))
 
правило = (условие команда ... выражение)
  | (условие => выражение)
Выполняет выражения по условиям. Если команда создаёт переменную, то эта переменная имеет область видимости только внутри блока с условием. В конструкции с => выражение должно возвращать функцию от одного аргумента, в эту функцию будет передан результат вычисления условия.

4.4 Символы🔗

функция

(символ? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является символом.

функция

(символ->строка символ)  строка?

  символ : символ?
Возвращает имя символа.

функция

(строка->символ строка)  символ?

  строка : строка?
Возвращает символ с заданным именем. Для одинаковых строк возвращает одинаковые символы.

4.5 Числа🔗

функция

(число? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является числом.

функция

(точное? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является точным числом.

функция

(неточное? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является неточным числом.

функция

(целое? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является целым числом. Внимание, неточное число тоже может быть целым!

функция

(точное-целое? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является точным целым числом.

функция

(целое-неотрицательное? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является точным целым неотрицательным числом.

функция

(вещественное? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является вещественным числом.

функция

(рациональное? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является рациональным числом.

функция

(комплексное? аргумент)  булево?

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

функция

(округлить число)  (одно-из целое? +inf.0 -inf.0 +nan.0)

  число : вещественное?
Возвращает целое, ближайшее к аргументу. Если число одно из +inf.0, -inf.0 или +nan.0, возвращает его же.

функция

(корень число)  число?

  число : число?
Возвращает главный (для положительных вещественных совпадает с арифметическим) квадратный корень из значения аргумента число. Результат точный, если число точное и квадратный корень из него рациональный.

функция

(синус число)  число?

  число : число?
Возвращает синус угла в радианах.

функция

(косинус число)  число?

  число : число?
Возвращает косинус угла в радианах.

функция

(тангенс число)  число?

  число : число?
Возвращает тангенс угла в радианах.

функция

(арксинус число)  число?

  число : число?
Возвращает арксинус в радианах.

функция

(арккосинус число)  число?

  число : число?
Возвращает арккосинус в радианах.

функция

(арктангенс число)  число?

  число : число?
Возвращает арктангенс в радианах.

функция

(экспонента число)  число?

  число : число?
Возвращает число Эйлера (e) в степени число.

функция

(логарифм число [основание])  число?

  число : число?
  основание : число? = (экспонента 1)
Возвращает натуральный логарифм. Если передано основание, то возвращает логарифм по этом основанию.

функция

(неточное->точное число)  точное?

  число : число?
Преобразовывает число в точное. Если число одно из +inf.0, -inf.0, +nan.0, +inf.f, -inf.f или +nan.f, тогда вызывается исключение.

функция

(точное->неточное число)  неточное?

  число : число?
Преобразовывает число в неточное.

функция

(небольшое-число? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является небольшим числом. Вычисления с небольшими числами выполняются быстрее.

функция

(абс число)  вещественное?

  число : вещественное?
Возвращает абсолютное значение аргумента.

4.6 Литеры🔗

функция

(литера? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является литерой.

функция

(литера->число аргумент)  точное-целое?

  аргумент : литера?
Возвращает код литеры.

функция

(число->литера аргумент)  литера?

  аргумент : точное-целое?
Возвращает литеру по коду.

4.7 Списки🔗

функция

(список? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является списком. Любой список также является парой.

функция

(пустой? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является пустым списком.

Пустой список.

функция

(список аргумент ...)  список?

  аргумент : любой
Возвращает список из произвольных значений.

функция

(пара? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является парой.

функция

(пара аргумент1 аргумент2)  пара?

  аргумент1 : любой
  аргумент2 : любой
Возвращает пару из переданных аргументов. Если второй аргумент список, то возвращаемое значение тоже список.

функция

(: аргумент1 аргумент2)  пара?

  аргумент1 : любой
  аргумент2 : любой
Аналогично функции пара возвращает пару из переданных аргументов. Если второй аргумент список, то возвращаемое значение тоже список.

функция

(первый аргумент)  любой

  аргумент : пара?
Возвращает первый элемент пары. Если пара является списком, то он же первый элемент списка.

функция

(оставшиеся аргумент)  любой

  аргумент : пара?
Возвращает второй элемент пары. Если пара является списком, то это список из всех элементов кроме первого (то есть «оставшиеся» элементы списка).

функция

(длина аргумент)  число?

  аргумент : список?
Возвращает количество элементов списка.

функция

(развернуть аргумент)  список?

  аргумент : список?
Возвращает список из значений аргумента в обратном порядке.

функция

(элемент-списка аргумент позиция)  любой

  аргумент : список?
  позиция : число?
Возвращает элемент списка в указанной позиции. Нумерация позиций начинается с нуля.

функция

(подсписок значение аргумент)  любой

  значение : любой
  аргумент : список?
Если в списке аргумент есть элемент значение, то возвращает хвост списка, начиная с этого элемента. Если нет, возвращает ложь.

Сравнение элементов с значением происходит при помощи ==.

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

функция

(подсписок=== значение аргумент)  любой

  значение : любой
  аргумент : список?
Функция полностью аналогична функции подсписок за исключением того, что для сравнения используется ===.

функция

(отобрать обработчик аргумент)  список?

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

функция

(отобразить обработчик аргумент ...)  список?

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

Вызов (отобразить ф список(а б в)) аналогичен (список ф(а) ф(б) ф(в))..

функция

(отобразить/и обработчик аргумент ...)  любой

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

Вызов (отобразить/и ф список(а б в)) аналогичен (&& ф(а) ф(б) ф(в)).

функция

(отобразить/или обработчик аргумент ...)  любой

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

Вызов (отобразить/или ф список(а б в)) аналогичен (|| ф(а) ф(б) ф(в)).

функция

(свернуть обработчик начальное аргумент ...)  список?

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

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

функция

(добавить аргумент ...)  список?

  аргумент : список?
(добавить аргумент ... последний-аргумент)  любой
  аргумент : список?
  последний-аргумент : любой
Возвращает сцепку переданных аргументов. Если все аргументы списки, тогда результатом является список, содержащий все элементы аргументов по порядку. Последний аргумент используется напрямую в хвостовой позиции.

Если последний аргумент не список, он всё равно используется в хвостовой позиции.

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

Время выполнения пропорционально сумме длин аргументов кроме последнего.

Примеры:

функция

(++ аргумент ...)  (один-из список? строка? массив? байты?)

  аргумент : (один-из список? строка? массив? байты?)
Возвращает сцепку переданных аргументов. Создаётся новая изменяемая коллекция достаточного размера для всех элементов аргументов, затем все элементы всех аргументов последовательно копируются в новую коллекцию. Тип аргументов должен быть одинаковый.

4.8 Строки🔗

функция

(строка? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является строкой.

функция

(новая-строка длина [литера])  строка?

  длина : целое-неотрицательное?
  литера : литера? = (число->литера 0)
Создаёт строку заданной длины и заполняет её указанным значением аргумента литера. Если литера не указана, заполняет литерой с нулевым кодом, то есть #\пусто.

функция

(длина-строки строка)  целое-неотрицательное?

  строка : строка?
Возвращает длину строки в литерах.

функция

(элемент-строки строка позиция)  литера?

  строка : строка?
  позиция : целое-неотрицательное?
Возвращает литеру на заданной позиции. Позиции нумеруются с нуля.

функция

(установить-элемент-строки! строка    
  позиция    
  литера)  пусто?
  строка : строка?
  позиция : целое-неотрицательное?
  литера : литера?
Устанавливает литеру на заданной позиции. Позиции нумеруются с нуля.

функция

(подстрока строка начало [конец])  строка?

  строка : строка?
  начало : целое-неотрицательное?
  конец : целое-неотрицательное? = (длина-строки строка)
Возвращает подстроку из аргумента строка с позиции начало по позицию конец.

функция

(добавить-строки строка ...)  строка?

  строка : строка?
Возвращает сцепку строк. Создаётся новая изменяемая строка достаточного размера, затем все литеры всех строк последовательно копируются в новую.

функция

(прописные строка)  строка?

  строка : строка?
Возвращает строку, в которой все литеры заменены на прописные.

функция

(строчные строка)  строка?

  строка : строка?
Возвращает строку, в которой все литеры заменены на строчные.

функция

(строки-равны? строка ...)  строка?

  строка : строка?
Возвращает истину, если все строки равны.

функция

(строки-возрастают? строка ...)  строка?

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

функция

(строки-не-убывают? строка ...)  строка?

  строка : строка?
Возвращает истину, если каждая следующая строка равна или больше предыдущей в лексикографическом (алфавитном) порядке.

функция

(строки-убывают? строка ...)  строка?

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

функция

(строки-не-возрастают? строка ...)  строка?

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

функция

(строки-равны?/без-регистра строка ...)  строка?

  строка : строка?
Возвращает истину, если все строки равны без учёта регистра.

функция

(строки-возрастают?/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если строки возрастают в лексикографическом (алфавитном) порядке без учёта регистра.

функция

(строки-не-убывают?/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если каждая следующая строка равна или больше предыдущей в лексикографическом (алфавитном) порядке без учёта регистра.

функция

(строки-убывают?/без-регистра строка ...)  строка?

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

функция

(строки-не-возрастают?/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если каждая следующая строка равна или меньше предыдущей в лексикографическом (алфавитном) порядке без учёта регистра.

функция

(прописные/местные строка)  строка?

  строка : строка?
Возвращает строку, в которой все литеры заменены на прописные с учётом региональных настроек.

функция

(строчные/местные строка)  строка?

  строка : строка?
Возвращает строку, в которой все литеры заменены на строчные с учётом региональных настроек.

функция

(строки-равны?/местные строка ...)  строка?

  строка : строка?
Возвращает истину, если все строки равны с учётом региональных настроек.

функция

(строки-возрастают?/местные строка ...)  строка?

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

функция

(строки-убывают?/местные строка ...)  строка?

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

функция

(строки-равны?/местные/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если все строки равны с учётом региональных настроек без учёта регистра.

функция

(строки-возрастают?/местные/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если строки возрастают в лексикографическом (алфавитном) порядке с учётом региональных настроек без учёта регистра.

функция

(строки-убывают?/местные/без-регистра строка    
  ...)  строка?
  строка : строка?
Возвращает истину, если строки убывают в лексикографическом (алфавитном) порядке с учётом региональных настроек без учёта регистра.

Следующие функции доступны только при использовании модуля строка.

функция

(строка-начинается-с? строка подстрока)  булево?

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

функция

(строка-заканчивается-на? строка подстрока)  булево?

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

4.9 Байты🔗

функция

(байт? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является целым точным числом в диапазоне 0..255.

функция

(байты? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является байтовой строкой.

функция

(новые-байты длина [значение])  байты?

  длина : целое-неотрицательное?
  значение : байт? = 0
Создаёт строку заданной длины и заполняет её указанным значением аргумента значение. Если значение не указано, заполняет числом 0.

функция

(длина-байтов байты)  целое-неотрицательное?

  байты : байты?
Возвращает длину байтовой строки в байтах.

функция

(элемент-байтов байты позиция)  байт?

  байты : байты?
  позиция : целое-неотрицательное?
Возвращает число на заданной позиции. Позиции нумеруются с нуля.

функция

(установить-элемент-байтов! байты    
  позиция    
  байт)  пусто?
  байты : байты?
  позиция : целое-неотрицательное?
  байт : байт?
Устанавливает число на заданной позиции. Позиции нумеруются с нуля.

функция

(байты->строка байты    
  [литера-ошибки    
  начало    
  конец])  строка?
  байты : байты?
  литера-ошибки : (одно-из литера? ложь) = ложь
  начало : целое-неотрицательное? = 0
  конец : целое-неотрицательное? = (длина-байтов байты)
Преобразует отрезок байтов в строку, трактуя байты в кодировке UTF-8. Если литера-ошибки не ложь, то она подставляется вместо байтов, не являющихся частью коректной последовательности, иначе вызывается исключение.

функция

(байты->строка/местные байты    
  [литера-ошибки    
  начало    
  конец])  строка?
  байты : байты?
  литера-ошибки : (одно-из литера? ложь) = ложь
  начало : целое-неотрицательное? = 0
  конец : целое-неотрицательное? = (длина-байтов байты)
Преобразует отрезок байтов в строку, трактуя байты в кодировке региональных настроек. Если литера-ошибки не ложь, то она подставляется вместо байтов, не являющихся частью коректной последовательности, иначе вызывается исключение.

4.10 Ввод-вывод🔗

функция

(порт-вывода? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является портом вывода.

функция

(порт-ввода? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является портом ввода.

функция

(порт? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является портом ввода или вывода.

функция

(записать аргумент [вывод])  пусто?

  аргумент : любой
  вывод : порт? = (текущий-порт-вывода)
Выводит значение аргумента в вывод таким образом, чтобы результат можно было прочитать обратно.

функция

(вывести аргумент [вывод])  пусто?

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

функция

(прочитать-строку [ввод] режим)  строка?

  ввод : порт? = (текущий-порт-ввода)
  режим : (один-из 'перенос 'возврат 'перенос-возврат 'любой 'любой-один)
Читает строку из порта ввод. Аргумент режим определяет разделитель строки:
  • 'перенос литера переноса #\перенос (с кодом 10);

  • 'возврат литера возврата каретки #\возврат (с кодом 13);

  • 'перенос-возврат пара литер перенос и возврат каретки;

  • 'любой любой из перечисленных выше;

  • 'любой-один перенос или возврат каретки, но не их комбинация.

Параметр, определяющий текущий порт вывода.

Параметр, определяющий текущий порт ввода.

параметр

(текущее-место)  (один-из строка? ложь)

(текущее-место место)  пусто?
  место : (один-из строка? ложь)
Параметр, определяющий текущее место (региональные настройки) для функций с суффиксом «/местные», например, строки-равны?/местные/без-регистра.

Когда этот параметр установлен в ложь, результат функций с суффиксом «/местные» должен быть переносим и совпадать с результатом функций без суффикса «/местные».

Значение "" является псевдонимом для региональных настроек операционной системы и является значением по умолчанию. Значение "C" (латинская) всегда доступно и для него результат совпадает с тем, который получен при значении ложь, для литер с кодами от 0 до 127 (цифры, латинский алфавит, ...).

Другие доступные имена мест определяются операционной системой.

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

4.11 Функции🔗

функция

(функция? аргумент)  булево?

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

4.12 Соответствия🔗

функция

(соответствие? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является соответствием.

функция

(создать-соответствие ассоциативный-список)  хеш?

  ассоциативный-список : (список-из пара?)
Возвращает соответствие, трактуя элементы из списка как пары ключ : значение.

4.13 Параметры🔗

функция

(параметр? аргумент)  булево?

  аргумент : любой
Возвращает истину, если аргумент является параметром.

функция

(параметр аргумент [охрана имя])  параметр?

  аргумент : любой
  охрана : (один-из (любой . -> . любой) ложь) = ложь
  имя : символ? = 'функция-параметра
Возвращает параметр с начальным значением аргумент. Если охрана не ложь, то когда функция параметра вызывается с аргументом, аргумент передаётся в функцию охрана, а уже результат этой функции записывается в параметр. Также охрана может вызывать исключение, если значение непримемлемо. К начальному значению эта функция не применяется.

синтаксис

(параметризуя ((выражение-параметра выражение-значения) ...)
              команда ... выражение)
 
выражение-параметра = параметр?
Выполняет переданные команды, возвращает результат выражения. Значения, полученные из выражений выражение-параметра, определяют, какие параметры устанавливать. Значения, полученные из выражений выражение-значения определяют их значения. Эти выражения вычисляются слева направо. Значения параметров связываются с соответствующими параметрами во время выполнения команд и выражения в теле формы. По окончании этой формы значения параметров остаются теми, которыми были до формы.

4.14 Модули🔗

синтаксис

(используется выражение-модуля ...)

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

4.15 Операционная система🔗

синтаксис

(замерить-время команда ... выражение)

Выполняет переданные команды, возвращает результат выражения. После выполнения выводит в текущий порт вывода строку «время процессора: {т1} реальное: {т2} сборки мусора: {т3}» c значениями времени выполнения переданных команд и выражения в полях {т1}..{т3}.

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

4.16 Приоритет операторов🔗

Оператор

 

Приоритет

++

 

6

==

 

5

&&

 

4

||

 

3

?

 

2

:

 

2, группировка справа

:=

 

1, группировка справа

=

 

0