Skip to content

Latest commit

 

History

History
378 lines (241 loc) · 8.41 KB

File metadata and controls

378 lines (241 loc) · 8.41 KB

IntRangeFormatter — контракт

Назначение

IntRangeFormatter преобразует последовательность целых чисел в нормализованную строку диапазонов.

Публичный API:

public static class IntRangeFormatter
{
    public static Result<string> Format(IEnumerable<int>? values, string? separator = ",");

    public static Result<string> Format(
        IEnumerable<int>? values,
        int maxRangeValue,
        string? separator = ",",
        bool useOpenRanges = true);
}

Методы не используют исключения для ошибок пользовательского ввода. Ошибки валидации возвращаются через Result<string>.


Общая семантика

Formatter принимает произвольную последовательность целых чисел и:

  1. валидирует значения;
  2. при необходимости сортирует их по возрастанию;
  3. удаляет дубликаты;
  4. объединяет соседние значения в диапазоны;
  5. возвращает строковое представление диапазонов.

Результат всегда нормализован:

  • значения идут по возрастанию;
  • дубликаты не повторяются;
  • соседние значения склеиваются в диапазоны.

Поддерживаемые входные последовательности

Методы принимают любые последовательности, реализующие IEnumerable<int>. Например:

  • List<int>
  • int[]
  • HashSet<int>
  • SortedSet<int>
  • LINQ-последовательности
  • yield-последовательности

Входная последовательность не обязана быть:

  • отсортированной;
  • уникальной;
  • материализованной заранее.

Формы записи результата

Одиночное значение

Если диапазон состоит из одного значения, оно записывается как:

N

Примеры:

  • [5] -> "5"
  • [0] -> "0"

Обычный диапазон

Если есть непрерывная последовательность значений, она записывается как:

N-M

Примеры:

  • [1,2,3] -> "1-3"
  • [0,1,2,3] -> "0-3"
  • [7,8,9,10] -> "7-10"

Перегрузка без maxRangeValue

Result<string> Format(IEnumerable<int>? values, string? separator = ",")

Эта перегрузка не использует открытые диапазоны. Она всегда форматирует диапазоны только в явном виде:

  • N
  • N-M

Примеры:

  • [1,2,3,5,7,8,9] -> "1-3,5,7-9"
  • [0,1,2,3] -> "0-3"

Перегрузка с maxRangeValue

Result<string> Format(
    IEnumerable<int>? values,
    int maxRangeValue,
    string? separator = ",",
    bool useOpenRanges = true)

Если useOpenRanges == true, formatter может использовать открытые диапазоны:

  • -N
  • N-
  • 0-

Правила использования открытых диапазонов

-N

Используется только для диапазона:

1..N

Примеры:

  • [1,2,3,4,5], maxRangeValue = 10 -> "-5"
  • [1], maxRangeValue = 10 -> "1"
    (-1 теоретически допустим, но одиночное значение formatter не превращает в диапазон)

N-

Используется для диапазона:

N..maxRangeValue

Примеры:

  • [10,11,12], maxRangeValue = 12 -> "10-"
  • [0,1,2,3], maxRangeValue = 3 -> "0-"

0-

Используется для диапазона:

0..maxRangeValue

Пример:

  • [0,1,2,3], maxRangeValue = 3 -> "0-"

Важное правило про 0

Formatter не использует -N для диапазона 0..N, потому что по контракту IntRangeParser запись -N означает именно 1..N.

То есть:

  • [0,1,2,3] -> "0-3" или "0-" при известном maxRangeValue
  • но не "-3"

Разделитель

Параметр separator задаёт строку, которая используется между токенами результата.

Примеры допустимых разделителей:

  • ","
  • ", "
  • ";"
  • " "
  • " | "

Ограничение для round-trip

Если нужен гарантированный round-trip с IntRangeParser, separator должен состоять только из символов, поддерживаемых парсером как разделители:

" ,.;_:#!|\\/'\""

Иначе formatter возвращает Failure.


Правила валидации

values == null

Возвращается Failure.

separator == null

Возвращается Failure.

separator == ""

Возвращается Failure.

Отрицательные значения

Любое значение < 0 недопустимо.

Возвращается Failure.

Перегрузка с maxRangeValue

Дополнительно:

  • maxRangeValue < 0 -> Failure
  • любое значение > maxRangeValue -> Failure

Нормализация входа

Перед форматированием последовательность нормализуется.

Сортировка

Если вход не отсортирован, formatter сортирует значения по возрастанию.

Пример:

  • [7,3,2,1,8,9,5] -> "1-3,5,7-9"

Удаление дублей

Если во входе есть повторы, они удаляются.

Пример:

  • [1,2,2,3,3,3,5] -> "1-3,5"

Примеры

Базовая перегрузка

Format(new[] { 1, 2, 3, 5, 7, 8, 9 })

Результат:

"1-3,5,7-9"

Неотсортированный вход

Format(new[] { 9, 8, 7, 5, 3, 2, 1 })

Результат:

"1-3,5,7-9"

Дубли

Format(new[] { 1, 2, 2, 3, 5, 5, 7 })

Результат:

"1-3,5,7"

Поддержка нуля

Format(new[] { 0, 1, 2, 3 })

Результат:

"0-3"

Использование открытых диапазонов

Format(new[] { 1, 2, 3, 4, 5 }, maxRangeValue: 10)

Результат:

"-5"
Format(new[] { 10, 11, 12 }, maxRangeValue: 12)

Результат:

"10-"
Format(new[] { 0, 1, 2, 3 }, maxRangeValue: 3)

Результат:

"0-"

Round-trip с IntRangeParser

Для корректного round-trip должны выполняться условия:

  1. использовать совместимый separator;
  2. использовать одинаковый maxRangeValue, если включены открытые диапазоны;
  3. учитывать, что formatter нормализует вход (сортировка + удаление дублей).

Пример:

var formatted = IntRangeFormatter.Format(new[] { 7, 3, 2, 1, 3, 8, 9, 5 }, ", ");
var parsed = IntRangeParser.Parse(formatted.Value, maxRangeValue: 100);

После round-trip результат будет:

[1,2,3,5,7,8,9]

Замечания по реализации

Formatter не использует bitset-подобные структуры. Для этой задачи достаточно:

  • собрать вход в список;
  • выполнить сортировку;
  • удалить дубли;
  • собрать диапазоны за один линейный проход;
  • сформировать строку через StringBuilder.

Такой алгоритм хорошо подходит как для обычных коллекций, так и для потоковых IEnumerable<int>.