Factor
- Русское название:
- Фактор
- Дата создания:
- 2003
- Создан под влиянием:
- Парадигма:
- Типизация:
- Принятые расширения файлов:
- .factor
- Реализации и версии (свернуть все | развернуть все):
Factor — современный конкатенативный язык программирования, поддерживающий функциональный и объектно-ориентированный стили программирования.
Язык был создан Славой Пестовым в 2003 году (реализация JFactor) как скриптовый язык для видеоигры. С тех пор язык претерпел существенные изменения — многие возможности добавлялись постепенно, когда их необходимость становилась очевидной.
Factor — конкатенативный язык: вызовы функций (“слов”) и арифметические действия используют постфиксный синтаксис вместо инфиксного или префиксного, как большинство языков. Программа выглядит как последовательность преобразований входных данных до получения результата; такой код называется конвейерным (pipeline code) и напоминает обработку данных в командных оболочках Unix.
Factor — стековый язык: для передачи аргументов в функции используется стек: параметры добавляются на стек, и затем вызванная функция извлекает их из стека по мере необходимости. Результат выполнения функции возвращается также через стек. В языке существуют именованные переменные, например, локальные в пределах функции или глобальные для хранения текущих потоков ввода-вывода, но большинство языковых конструкций не требует их использования. Входные и выходные данные всех функций описываются формально при помощи так называемой записи действий со стеком (stack effect notation) (см. примеры).
Factor поддерживает функции высшего порядка — комбинаторы и анонимные функции, состоящие из набора слов, окруженных квадратными скобками, — цитаты. Цитаты могут храниться в стеке, не выполняясь при этом, и затем использоваться комбинаторами в качестве аргументов. Все управляющие конструкции языка, включая циклы и ветвление, реализованы как комбинаторы. Кроме того, комбинаторы заменяют слова изменения порядка элементов стека, использующиеся в других стековых языках.
Основные комбинаторы языка (аргументы перечислены в том порядке, в котором они должны быть добавлены в стек):
-
if
выполняет заданные действия в зависимости от значения логической переменной. Аргументы: логическое значение-условие, then-цитата, else-цитата. Варианты комбинатора —when
(без ветвиelse
),unless
(без ветвиthen
). -
each
соответствует циклуforeach
в других языках: он перебирает все элементы массива и к каждому применяет заданную цитату. Аргументы: массив, цитата-тело цикла. -
reduce
— вариант цикла, позволяющий накапливать значение между итерациями. Аргументы: массив, начальное значение аккумулятора и цитату-тело цикла, которая использует в качестве параметров очередной элемент массива и текущее значение аккумулятора. -
map
преобразует один массив в другой применением к каждому элементу определенной функции. Аргументы: исходный массив, преобразующая функция-цитата. -
filter
отбирает в новый массив те из элементов старого, которые удовлетворяют условию, заданному в виде цитаты. Аргументы: исходный массив, цитата-условие. -
cleave
принимает на вход значение и массив цитат и применяет к этому значению все элементы массива, получая серию значений. Для этого и следующих комбинаторов в случае, когда цитат две или три, применяются сокращенные формы комбинаторов, позволяющие избежать лишних фигурных скобок (выделение массива). Сокращенные формы —bi
иtri
. -
spread
принимает на вход серию значений и массив цитат (одинакового размера) и применяет каждую цитату к соответствующему значению. Сокращенные формы —bi*
иtri*
. -
napply
принимает на вход серию значений, цитату и число и применяет эту цитату к каждому значению (параметр-число задает количество значений в серии). Сокращенные формы —bi@
иtri@
.
Factor разделяет систему модулей (организация исходного кода) и систему типов/классов (организация логики обработки данных). Код программ на Factor организован как система словарей — вложенных модулей (аналогично пакетам в Java), соответствующих файловой структуре кода.
Объектно-ориентированная парадигма Factor основана на обобщенных функциях, реализация которых зависит от типов их аргументов. Factor — чисто объектно-ориентированный язык в том смысле, что любое значение является объектом, и базовые операции выполняются посредством вызова методов.
В Factor реализована поддержка метапрограммирования, системы макросов и функторов, что позволяет легко расширять возможности языка. Кроме того, Factor включает ряд инструментов для низкоуровневого программирования, в т.ч. интерфейсы для процедур, написанных на других языках, функции для работы с бинарными данными и механизм автоматической сборки мусора.
Элементы синтаксиса:
Комментарий до конца строки | ! |
---|---|
Регистрозависимость | да |
Равенство | eq? |
Тождественное равенство | = |
Сравнение | <=> |
Определение функции | : foo ( <input> -- <output> ) <body> ; |
Вызов функции | foo |
Если - то | <condition> [ <body> ] when |
Если - то - иначе | <condition> [ <if-branch> ] [ <else-branch> ] if |
Логотип Factor
Примеры:
Hello, World!:
Пример для версий Factor 0.94Первая строка импортирует библиотеку io
, содержащую слово print
. Вторая строка добавляет на стек строку-сообщение и затем вызывает слово print
, которое выводит верхний элемент стека на печать.
USE: io
"Hello, World!" print
Факториал:
Пример для версий Factor 0.94В первой строке перечисляются необходимые библиотеки: formatting
(printf
), kernel
(dup
), math
(арифметические действия) и sequences
(iota
). Во второй строке объявляется имя текущего словаря — это необходимо для определения новых слов.
Затем определяется слово factorial
, которое заменяет на стеке целое число n
на его факториал. Для этого оно строит массив чисел от 0 до n — 1
с помощью слова iota
и сворачивает его с единицей цитатой [ 1 + * ]
(прибавление 1 и перемножение двух чисел) и комбинатором reduce
.
Слово main
генерирует массив чисел от 0 до 16 и комбинатором each
применяет к ним цитату, которая вычисляет факториалы чисел и выводит их на печать.
В стандартном словаре math.combinatorics
присутствует слово factorial
, определенное указанным в примере образом.
USING: formatting kernel math sequences ;
IN: factorial-example
: factorial ( n -- n! )
iota 1 [ 1 + * ] reduce ;
17 iota
[ dup factorial "%d! = %d\n" printf ] each
Числа Фибоначчи:
Пример для версий Factor 0.94В этом примере используется рекурсивное вычисление чисел Фибоначчи. Слово fib
вычисляет n-ое число Фибоначчи: если аргумент не больше 1, он остается на стеке в качестве возвращаемого значения, иначе заменяется на сумму предыдущих чисел Фибоначчи. Слово bi
является сокращенной формой комбинатора cleave
и позволяет применить две цитаты (в данном случае вызовы слова fib
для меньших аргументов) к одному элементу стека (n
).
USING: formatting kernel math sequences ;
IN: fibonacci-example
: fib ( n -- fib(n) )
dup
1 >
[ [ 1 - fib ] [ 2 - fib ] bi + ]
when ;
16 iota [ 1 + fib "%d, " printf ] each
"...\n" printf
Факториал:
Пример для версий Factor 0.94В этом примере используется чисто рекурсивное вычисление факториала. Слово factorial
заменяет на стеке n
на n!
с побочным эффектом: выводом всех значений факториалов от 0 до n. После выполнения комбинатора if
на стеке остаются значения n
и n!
, которые словами swap
и over
заменяются на n!
, n
, n!
; два последних значения используются при выводе, первое остается на стеке в качестве возвращенного.
В основной части программы main
приходится добавить drop
значения 16!
, чтобы сигнатура программы оставалась ( -- )
.
USING: formatting kernel math ;
IN: factorial-example
: factorial ( n -- n! )
dup
0 =
[ 1 ]
[ dup dup 1 - factorial * ]
if
swap over "%d! = %d\n" printf ;
16 factorial
drop
Квадратное уравнение:
Пример для версий Factor 0.94Слово quadratic-equation
принимает на вход коэффициенты квадратного уравнения и выводит результаты его решения на печать, ничего не возвращая. Обратим внимание на то, что это слово определено не через токен :
, как обычные слова, а через ::
— это означает, что в нем будут использоваться именованные переменные с лексической областью видимости, в данном случае именованные параметры a
, b
и c
и локальные переменные d
, x0
и sd
(определенные оператором :>
). Такие переменные можно загружать на стек по их имени. Слова для работы с именованными переменными доступны в словаре locals
.
В Factor есть встроенный тип данных для комплексных чисел; если дискриминант отрицателен, корень из него sd
автоматически примет тип complex
. В этом случае для вывода корней на печать используются слова real-part
и imaginary-part
, извлекающие действительную и мнимую части комплексного числа, соответственно.
readln
читает строку из потока ввода, а string>number
(словарь math.parser
) преобразует строку в действительное число.
USING: formatting io kernel locals math math.functions math.parser ;
IN: quadratic-example
:: quadratic-equation ( a b c -- )
a 0 =
[ "Not a quadratic equation." printf ]
[ b sq a c * 4 * - :> d
b neg a 2 * / :> x0
d sqrt a 2 * / :> sd
d 0 =
[ x0 "x = %f\n" printf ]
[ d 0 >
[ x0 sd + x0 sd - "x1 = %f\nx2 = %f\n" printf ]
[ x0 sd + [ real-part ] [ imaginary-part ] bi "x1 = (%f, %f)\n" printf
x0 sd - [ real-part ] [ imaginary-part ] bi "x2 = (%f, %f)\n" printf ]
if
]
if
]
if ;
readln string>number
readln string>number
readln string>number
quadratic-equation
CamelCase:
Пример для версий Factor 0.94В этом примере используются регулярные выражения. Слово re-split
(словарь regexp
) разбивает строку на массив подстрок, разделенных заданным регулярным выражением. Затем комбинатор map
применяет к каждому элементу массива слово >title
(unicode.case
), которое переводит первый символ строки в верхний регистр, а остальные — в нижний. Наконец, join
(sequences
) конкатенирует строки массива в одну с разделителем “”.
USING: kernel io regexp sequences unicode.case ;
readln R/ [^a-zA-Z]+/ re-split
[ >title ] map
"" join print
Факториал:
Пример для версий Factor 0.94В этом примере используется встроенное слово factorial
, определенное в библиотеке math.combinatorics
.
USING: formatting kernel math.combinatorics sequences ;
17 iota [ dup factorial "%d! = %d\n" printf ] each
Комментарии
]]>blog comments powered by Disqus
]]>