Примеры программ

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

О разделе

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

Все примеры расположены в директории examples/:

Содержание

  1. Привет, Мир!
  2. Факториал
  3. Числа Фибоначчи
  4. Алгоритм Евклида (НОД)
  5. Поиск элемента в списке
  6. Цикл и генератор последовательности
  7. Асинхронные вычисления
  8. Монада успеха/ошибки
  9. Пользовательские типы данных
  10. Логические операции
  11. Пайплайн-оператор
  12. Недетерминированные вычисления (List-монада)
  13. Каррирование и частичное применение
  14. Сопоставление с образцом
  15. Импорт модулей

1. Привет, Мир!

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

Глаголица

вывод("Привет Мир!")

F#

printfn "%s" "Привет Мир!"

Функция вывод — это встроенный аналог printfn из F#, она автоматически добавляет перевод строки и умеет печатать значения любого типа.

2. Факториал

Классический пример рекурсии. На Глаголице используется ключевое слово ПУСТЬ для объявления функции, а условные выражения записываются через ЕСЛИ ... ТО ... ИНАЧЕ.

Глаголица

ПУСТЬ факториал(х){
    ЕСЛИ х == 0 ТО { 1 }
    ИНАЧЕ { х * факториал(х - 1) }
}
вывод(факториал(5))

F#

let rec factorial x =
    if x = 0 then 1
    else x * factorial (x - 1)
printfn "%A" (factorial 5)

В Глаголице рекурсивные функции не требуют отдельного модификатора (как rec в F#) — функция автоматически видит саму себя.

3. Числа Фибоначчи

Ещё один пример рекурсии, демонстрирующий вложенные условия.

Глаголица

ПУСТЬ фиб(х){
    ЕСЛИ х == 0 ТО { 0 }
    ИНАЧЕ {
        ЕСЛИ х == 1 ТО { 1 }
        ИНАЧЕ { фиб(х - 1) + фиб(х - 2) }
    }
}
вывод(фиб(10))

F#

let rec fib x =
    if x <= 1 then x
    else fib (x - 1) + fib (x - 2)
printfn "%A" (fib 10)

4. Алгоритм Евклида (НОД)

Рекурсивный алгоритм нахождения наибольшего общего делителя через вычитание.

Глаголица

ПУСТЬ нод(а, б){
    ЕСЛИ а == б ТО { а }
    ИНАЧЕ {
        ЕСЛИ а > б ТО { нод(а - б, б) }
        ИНАЧЕ { нод(а, б - а) }
    }
}
вывод(нод(14, 21))

F#

let rec gcd a b =
    if a = b then a
    elif a > b then gcd (a - b) b
    else gcd a (b - a)
printfn "%A" (gcd 14 21)

Демонстрирует сопоставление с образцом (СОПОСТАВИТЬ ... С) и работу со списками. Список разбирается на голову и хвост через шаблон [голова - хвост].

Глаголица

ПУСТЬ содержит(список, элемент){
    СОПОСТАВИТЬ список С {
        КОГДА ПУСТО -> { ЛОЖЬ }
        КОГДА [голова - хвост] -> {
            ЕСЛИ голова == элемент ТО { ИСТИНА }
            ИНАЧЕ { содержит(хвост, элемент) }
        }
    }
}
ПУСТЬ мой_список = [10, 20, 30]
вывод(содержит(мой_список, 20))

F#

let rec contains lst element =
    match lst with
    | [] -> false
    | head :: tail ->
        if head = element then true
        else contains tail element

let myList = [10; 20; 30]
printfn "%A" (contains myList 20)

Соответствия:

ГлаголицаF#
СОПОСТАВИТЬ x С { ... }match x with
КОГДА ПУСТО -> { ... }| [] -> ...
КОГДА [голова - хвост]| head :: tail ->
ИСТИНА / ЛОЖЬtrue / false

6. Цикл и генератор последовательности

Цикл ДЛЯ пробегает по диапазону, сгенерированному функцией ОТ_ДО. Это аналог for ... in 1..10 do в F#.

Глаголица

ПУСТЬ таблица = ДЛЯ (ч В ОТ_ДО(1, 10)) {
    вывод(ч * ч)
}

F#

for i in 1..10 do
    printfn "%d" (i * i)

7. Асинхронные вычисления

Глаголица поддерживает асинхронные блоки через ФОНОМ({ ... }) и ожидание результата через ДОЖДАТЬСЯ(...). Это прямой аналог async { ... } и Async.RunSynchronously в F#.

Глаголица

ПУСТЬ долгая_задача = ФОНОМ({
    ЕСЛИ ИСТИНА ТО { 42 } ИНАЧЕ { 0 }
})
ПУСТЬ результат = ДОЖДАТЬСЯ(долгая_задача)
вывод(результат)

F#

let task = async {
    return if true then 42 else 0
}
let asyncResult = Async.RunSynchronously task
printfn "%A" asyncResult

8. Монада успеха/ошибки

Для безопасной обработки результатов вычислений Глаголица предоставляет монадические конструкторы УСПЕХ(...) и оператор связывания ДАЛЕЕ_УСПЕШНО. Это аналог Result<_,_> и Result.bind в F#.

Глаголица

ПУСТЬ старт = УСПЕХ(10)
ПУСТЬ результат = старт ДАЛЕЕ_УСПЕШНО (х) -> {
    УСПЕХ(х * 2)
}
вывод(результат)

F#

let start : Result = Ok 10
let res = start |> Result.bind (fun x -> Ok(x * 2))
printfn "%A" res

Оператор ДАЛЕЕ_УСПЕШНО пропускает вычисление дальше только в случае успеха; при ошибке цепочка прерывается.

9. Пользовательские типы данных

Глаголица позволяет определять структурные типы (записи) через ключевое слово ТИП. Поля доступны через точечную нотацию.

Глаголица

ТИП Игрок = { здоровье Int, уровень Int }
ПУСТЬ герой = Игрок { здоровье = 100, уровень = 5 }

ПУСТЬ жив_ли(персонаж){
    ЕСЛИ персонаж.здоровье > 0 ТО { ИСТИНА } ИНАЧЕ { ЛОЖЬ }
}
вывод(жив_ли(герой))

F#

type Player = { Health: int; Level: int }
let hero = { Health = 100; Level = 5 }

let isAlive p =
    if p.Health > 0 then true else false
printfn "%A" (isAlive hero)

10. Логические операции

Логические И, ИЛИ, НЕ — это аналоги &&, ||, not в F#.

Глаголица

ПУСТЬ проверка(а, б){
    ЕСЛИ а И б ТО { ИСТИНА }
    ИНАЧЕ { ЛОЖЬ }
}
вывод(проверка(ИСТИНА, ЛОЖЬ))

F#

let check a b =
    if a && b then true
    else false
printfn "%A" (check true false)

11. Пайплайн-оператор

Оператор ДАЛЕЕ передаёт значение слева в анонимную функцию справа — как |> в F#. Это упрощает чтение цепочек преобразований.

Глаголица

ПУСТЬ модификатор(х){ х * 5 }
ПУСТЬ результат = 10 ДАЛЕЕ (значение) -> {
    модификатор(значение)
}
вывод(результат)

F#

let modifier x = x * 5
let pipeResult = 10 |> (fun v -> modifier v)
printfn "%A" pipeResult

12. Недетерминированные вычисления (List-монада)

Конструкция ВАРИАНТЫ(...) создаёт недетерминированное значение — программа исполняется для каждой комбинации возможных значений. Условие ТРЕБОВАТЬ(...) отсеивает неподходящие варианты. Это удобный способ выразить декартово произведение и фильтрацию.

Глаголица

ПУСТЬ х = ВАРИАНТЫ(1, 2, 3)
ПУСТЬ у = ВАРИАНТЫ(1, 2, 3)
ТРЕБОВАТЬ(х + у == 4)
вывод(х)

F#

let combinations =
    [ for x in 1..3 do
        for y in 1..3 do
            if x + y = 4 then yield x ]
printfn "%A" combinations

В F# подобный эффект достигается через list comprehension с вложенными циклами и условием.

13. Каррирование и частичное применение

В Глаголице можно применить функцию частично, подставив _ вместо ещё не известного аргумента — получится новая функция от оставшихся аргументов.

Глаголица

ПУСТЬ вычесть(п, р){ п - р }
ПУСТЬ отнятьОтДесяти = вычесть(10, _)
вывод(отнятьОтДесяти(3))

F#

let subtract a b = a - b
let subtractFromTen = subtract 10
printfn "%d" (subtractFromTen 3)

В F# каррирование автоматическое (все функции каррированы по умолчанию), а в Глаголице оно явное — через placeholder _.

14. Сопоставление с образцом

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

Глаголица

ПУСТЬ описатьЧисло(х){
    СОПОСТАВИТЬ х С {
        КОГДА 0 -> { "Это ноль" }
        КОГДА 1 -> { "Это один" }
        КОГДА _ -> { "Это другое число" }
    }
}
вывод(описатьЧисло(1))

ПУСТЬ проверитьСписок(сп){
    СОПОСТАВИТЬ сп С {
        КОГДА ПУСТО -> { "Список пустой" }
        КОГДА [голова - хвост] -> { "В списке есть элементы" }
    }
}
вывод(проверитьСписок([1, 2, 3]))

F#

let describeNumber x =
    match x with
    | 0 -> "Это ноль"
    | 1 -> "Это один"
    | _ -> "Это другое число"

printfn "%s" (describeNumber 1)

let checkList lst =
    match lst with
    | [] -> "Список пустой"
    | head :: tail -> "В списке есть элементы"

printfn "%s" (checkList [1; 2; 3])

15. Импорт модулей

Глаголица поддерживает разделение программ на модули с помощью директивы ИМПОРТ. После импорта все функции и значения из подключённого файла становятся доступны в текущем.

Файл с функцией:

ПУСТЬ факториал(х){
    ЕСЛИ х == 0 ТО { 1 }
    ИНАЧЕ { х * факториал(х - 1) }
}

Файл, который импортирует:

Глаголица

ИМПОРТ ".\examples\Glagol_examples\factorial_for_import.gl"

ПУСТЬ результат = { факториал(5) }
вывод(результат)

F#

#load "factorial_for_import.fsx"
open Factorial_for_import

let result = factorial 5
printfn "%d" result

В Глаголице используется ИМПОРТ "путь/к/файлу.gl", в F# — директива #load вместе с open.

Сводная таблица соответствий

КонструкцияГлаголицаF#
Объявление значенияПУСТЬ имя = ...let имя = ...
Объявление функцииПУСТЬ ф(х){ ... }let ф x = ...
УсловиеЕСЛИ ... ТО ... ИНАЧЕif ... then ... else
Логические операторыИ, ИЛИ, НЕ&&, ||, not
ЛитералыИСТИНА, ЛОЖЬ, ПУСТОtrue, false, []
СопоставлениеСОПОСТАВИТЬ ... Сmatch ... with
Шаблон списка[голова - хвост]head :: tail
ЦиклДЛЯ (х В ОТ_ДО(а,б))for x in a..b do
Пайплайнзначение ДАЛЕЕ фзначение |> ф
АсинхронностьФОНОМ({...}), ДОЖДАТЬСЯasync { ... }, RunSynchronously
Монада успехаУСПЕХ, ДАЛЕЕ_УСПЕШНОOk, Result.bind
НедетерминизмВАРИАНТЫ, ТРЕБОВАТЬlist comprehension с условиями
Структурный типТИП И = { поле Тип }type T = { Field: Type }
ИмпортИМПОРТ "путь"#load "путь" + open
Вывод в консольвывод(...)printfn "..." ...
Комментарийкомментарий: ...// ...

Как запускать примеры

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