Inspect.Algebra
Набор функций для создания и обработки документов алгебры.
Этот модуль реализует функциональность, описанную в «Strictly Pretty» (2000) Кристиана Линдига с небольшими дополнениями, такими как поддержка узлов String и пользовательская функция отрисовки, которая максимизирует использование горизонтального пространства.
iex> Inspect.Algebra.empty :doc_nil iex> "foo" "foo"
С помощью функций этого модуля мы можем объединять различные элементы и отрисовывать их:
iex> doc = Inspect.Algebra.concat(Inspect.Algebra.empty, "foo") iex> Inspect.Algebra.format(doc, 80) ["foo"]
Функции nest/2, space/2 и line/2 помогут вам структурировать документ. Однако алгебра документов становится интересной при использовании функций, таких как break/1, которая преобразует заданную строку в разрыв строки в зависимости от имеющегося места для печати. Давайте склеим два документа вместе с разрывом и затем отрисуем их:
iex> doc = Inspect.Algebra.glue("a", " ", "b")
iex> Inspect.Algebra.format(doc, 80)
["a", " ", "b"] Обратите внимание, что разрыв был представлен так же, как он есть, так как мы не достигли ограничения строки. Как только это произойдёт, он заменяется символом новой строки:
iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
iex> Inspect.Algebra.format(doc, 10)
["aaaaaaaaaaaaaaaaaaaa", "\n", "b"] Наконец, этот модуль также содержит функции, связанные с Elixir, в некоторой степени связанные с форматированием Elixir, а именно surround/3 и surround_many/5.
Детали реализации
Оригинальная реализация алгоритма на Haskell Wadler полагается на ленивые вычисления для развертывания групп документов на двух альтернативах: :flat (разрывы как пробелы) и :break (разрывы как новые строки). Реализация того же логики на строгом языке, таком как Elixir, приводит к экспоненциальному росту возможных документов, если группы документов не закодированы явно как :flat или :break. Эти группы затем сводятся к простому документу, где макет уже определен, согласно Lindig.
Эта реализация немного изменяет семантику алгоритма Линдига, чтобы разрешить элементам, принадлежащим к одной группе, печататься вместе в одной строке, даже если они не помещаются в строку полностью. Это было достигнуто путем изменения :break для обозначения возможного разрыва и :flat для принудительного создания плоской структуры. Затем определение того, работает ли разрыв как новая строка, сводится к проверке, достаточно ли места до следующего разрыва, который не находится внутри группы (которая всё ещё плоская).
Пользовательские красивые принтеры могут быть реализованы с использованием документов, возвращаемых этим модулем, и путем предоставления собственных функций отрисовки.
Резюме
Типы
- t()
Функции
- break()
-
Возвращает сущность документа, представляющую стандартный разрыв
- break(string)
-
Возвращает сущность документа, представляющую разрыв, основанный на заданном
string - color(doc, color_key, opts)
-
Изменяет цвет документа, если
color_keyимеет цвет в опциях - concat(docs)
-
Объединяет список документов, возвращая новый документ
- concat(doc1, doc2)
-
Объединяет две сущности документа, возвращая новый документ
- empty()
-
Возвращает сущность документа, используемую для представления пустоты
- fold_doc(docs, folder_fun)
-
Складывает список документов в документ с использованием заданной функции складывания
- format(doc, width)
-
Форматирует заданный документ для заданной ширины
- glue(doc1, doc2)
-
Объединяет два документа, вставляя между ними стандартный разрыв
- glue(doc1, break_string, doc2)
-
Объединяет два документа (
doc1иdoc2) вместе, вставляя между ними заданный разрывbreak_string - group(doc)
-
Возвращает группу, содержащую указанный документ
doc - line(doc1, doc2)
-
Вставляет обязательный разрыв строки между двумя документами
- nest(doc, level)
-
Вкладывает данный документ на заданный
levelуровень - space(doc1, doc2)
-
Вставляет обязательный одиночный пробел между двумя документами
- surround(left, doc, right)
-
Оборачивает документ символами
- surround_many(left, docs, right, opts, fun, separator \\ ",")
-
Применяет отображение и объединяет коллекцию элементов
- to_doc(term, opts)
-
Преобразует термин Elixir в документ алгебры в соответствии с протоколом
Inspect
Типы
t()
t() :: :doc_nil | :doc_line | doc_cons() | doc_nest() | doc_break() | doc_group() | doc_color() | binary()
Функции
break()
break() :: doc_break()
Возвращает сущность документа, представляющую стандартный разрыв.
То же самое, что вызов break/1 со стандартным разрывом.
break(string)
break(binary()) :: doc_break()
Возвращает сущность документа, представляющую разрыв, основанный на заданном string.
Этот разрыв может быть представлен как разрыв строки или как заданная string, в зависимости от mode выбранной компоновки или предоставленного разделителя.
Примеры
Давайте создадим документ, объединив две строки с разрывом между ними:
iex> doc = Inspect.Algebra.concat(["a", Inspect.Algebra.break("\t"), "b"])
iex> Inspect.Algebra.format(doc, 80)
["a", "\t", "b"] Обратите внимание, что разрыв был представлен заданной строкой, так как мы не достигли ограничения строки. Как только это произойдёт, он будет заменён новой строкой:
iex> break = Inspect.Algebra.break("\t")
iex> doc = Inspect.Algebra.concat([String.duplicate("a", 20), break, "b"])
iex> Inspect.Algebra.format(doc, 10)
["aaaaaaaaaaaaaaaaaaaa", "\n", "b"] color(doc, color_key, opts)
color(t(), Inspect.Opts.color_key(), Inspect.Opts.t()) :: doc_color()
Изменяет цвет документа, если color_key имеет цвет в опциях.
concat(docs)
concat([t()]) :: t()
Объединяет список документов, возвращая новый документ.
Примеры
iex> doc = Inspect.Algebra.concat(["a", "b", "c"]) iex> Inspect.Algebra.format(doc, 80) ["a", "b", "c"]
concat(doc1, doc2)
concat(t(), t()) :: t()
Объединяет две сущности документа, возвращая новый документ.
Примеры
iex> doc = Inspect.Algebra.concat("hello", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", "world"] empty()
empty() :: :doc_nil
Возвращает сущность документа, используемую для представления пустоты.
Примеры
iex> Inspect.Algebra.empty :doc_nil
fold_doc(docs, folder_fun)
fold_doc([t()], (t(), t() -> t())) :: t()
Складывает список документов в документ с использованием заданной функции складывания.
Список документов складывается «справа»; в этом случае эта функция похожа на List.foldr/3, за исключением того, что не ожидает начального аккумулятора и использует последний элемент docs в качестве начального аккумулятора.
Примеры
iex> docs = ["A", "B", "C"] iex> docs = Inspect.Algebra.fold_doc(docs, fn(doc, acc) -> ...> Inspect.Algebra.concat([doc, "!", acc]) ...> end) iex> Inspect.Algebra.format(docs, 80) ["A", "!", "B", "!", "C"]
format(doc, width)
format(t(), non_neg_integer() | :infinity) :: iodata()
Форматирует заданный документ для заданной ширины.
Принимает максимальную ширину и документ для печати в качестве аргументов и возвращает представление данных IO лучшего макета для документа, чтобы он поместился в заданную ширину.
Примеры
iex> doc = Inspect.Algebra.glue("hello", " ", "world")
iex> Inspect.Algebra.format(doc, 30) |> IO.iodata_to_binary()
"hello world"
iex> Inspect.Algebra.format(doc, 10) |> IO.iodata_to_binary()
"hello\nworld" glue(doc1, doc2)
glue(t(), t()) :: t()
Объединяет два документа, вставляя между ними стандартный разрыв.
Разрыв, который вставляется между left и right, - это тот, который возвращается функцией break/0.
Примеры
iex> doc = Inspect.Algebra.glue("hello", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", " ", "world"] glue(doc1, break_string, doc2)
glue(t(), binary(), t()) :: t()
Объединяет два документа (doc1 и doc2) вместе, вставляя между ними заданный разрыв break_string.
Подробнее о том, как вставляется разрыв, см. break/1.
Примеры
iex> doc = Inspect.Algebra.glue("hello", " ", "world")
iex> Inspect.Algebra.format(doc, 80)
["hello", " ", "world"] group(doc)
group(t()) :: doc_group()
Возвращает группу, содержащую указанный документ doc.
Примеры
iex> doc = Inspect.Algebra.group( ...> Inspect.Algebra.concat( ...> Inspect.Algebra.group( ...> Inspect.Algebra.concat( ...> "Hello,", ...> Inspect.Algebra.concat( ...> Inspect.Algebra.break, ...> "A" ...> ) ...> ) ...> ), ...> Inspect.Algebra.concat( ...> Inspect.Algebra.break, ...> "B" ...> ) ...> )) iex> Inspect.Algebra.format(doc, 80) ["Hello,", " ", "A", " ", "B"] iex> Inspect.Algebra.format(doc, 6) ["Hello,", "\n", "A", " ", "B"]
line(doc1, doc2)
line(t(), t()) :: t()
Вставляет обязательный разрыв строки между двумя документами.
Примеры
iex> doc = Inspect.Algebra.line("Hughes", "Wadler")
iex> Inspect.Algebra.format(doc, 80)
["Hughes", "\n", "Wadler"] nest(doc, level)
nest(t(), non_neg_integer()) :: doc_nest()
Вкладывает данный документ на заданный level уровень.
Вложение будет добавлено к разрывам строк.
Примеры
iex> doc = Inspect.Algebra.nest(Inspect.Algebra.glue("hello", "world"), 5)
iex> Inspect.Algebra.format(doc, 5)
["hello", "\n ", "world"] space(doc1, doc2)
space(t(), t()) :: t()
Вставляет обязательный одиночный пробел между двумя документами.
Примеры
iex> doc = Inspect.Algebra.space("Hughes", "Wadler")
iex> Inspect.Algebra.format(doc, 5)
["Hughes", " ", "Wadler"] surround(left, doc, right)
surround(t(), t(), t()) :: t()
Окружает документ символами.
Помещает заданный документ doc между документами left и right, заключая и вложенно его. Документ помечается как группа, чтобы максимально компактно показать все вместе.
Примеры
iex> doc = Inspect.Algebra.surround("[", Inspect.Algebra.glue("a", "b"), "]")
iex> Inspect.Algebra.format(doc, 3)
["[", "a", "\n ", "b", "]"] surround_many(left, docs, right, opts, fun, separator \\ ",")
surround_many(t(), [any()], t(), Inspect.Opts.t(), (term(), Inspect.Opts.t() -> t()), t()) :: t()
Отображает и склеивает коллекцию элементов.
Использует заданные документы left и right в качестве окружающих, а документ-разделитель separator для разделения элементов в docs. Можно передать ограничение: когда это ограничение достигнуто, функция перестаёт склеивать и выводит "..." вместо этого.
Примеры
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
...> %Inspect.Opts{limit: :infinity}, fn i, _opts -> to_string(i) end)
iex> Inspect.Algebra.format(doc, 5) |> IO.iodata_to_binary
"[1,\n 2,\n 3,\n 4,\n 5]"
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
...> %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end)
iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
"[1, 2, 3, ...]"
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
...> %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end, "!")
iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
"[1! 2! 3! ...]" to_doc(term, opts)
to_doc(any(), Inspect.Opts.t()) :: t()
Преобразует терм Elixir в документ алгебры в соответствии с протоколом Inspect.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.4.5/Inspect.Algebra.html