Библиотека или алгоритм для разнесения буквенно-цифрового диапазона

Мне было интересно, есть ли библиотека или алгоритм с открытым исходным кодом, который может расширить нечисловой диапазон. Например, если у вас 1A до 9A, вы должны получить

1A, 2A, 3A, 4A, 5A, 6A, 7A, 8A, 9A.

Я пробовал использовать Google для этого, и лучшее, что я мог придумать, было Regex, которое расширяло числа с помощью тире (1-3, становясь 1,2,3).

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
798
4

Ответы 4

Я пытался оставить его в некотором роде открытым, потому что количество возможностей ошеломляет. Я считаю, что это один из тех вопросов, на который нельзя ответить на 100% здесь, не вдаваясь в множество технических подробностей, который считается «хорошим» или «плохим» диапазоном. Я просто пытаюсь найти точку перехода для идей о том, как другие люди решили эту проблему. Я надеялся, что кто-то написал в блоге сообщение, объясняющее, как они решили эту проблему, или создал целую библиотеку для решения этой проблемы.

Я бы сказал, что первым шагом в решении будет определение того, как символы и числа взаимодействуют и образуют последовательность. Данный пример не ясен, поскольку вы, по крайней мере, предполагаете, что он запускает 1A, 1B .... 8Y, 8Z, 9A - это предполагает, что ваш ввод ограничен десятичным числом, за которым следует один символ.

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

Например, вы можете предположить, что каждый символ на входе является одним из (1-9A-Z), поэтому вы можете легко сделать это непрерывным, взяв десятичное значение ascii альфа-символов и вычтя 55, что фактически даст вам диапазон (1-35)

Если мы предположим, что начальный и конечный диапазоны будут следовать одному и тому же чередующемуся шаблону, и ограничим диапазон цифр 0-9 и A-Z, мы можем рассматривать каждую группу цифр как компонент в многомерной координате. Например, 1A будет соответствовать двумерной координате (1,A) (это то, что Excel использует для маркировки своей двумерной сетки строк и столбцов); тогда как AA1BB2 будет четырехмерной координатой (AA,1,BB,2).

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

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

IEnumerable<string> ExpandRange( string start, string end ) {
  // Split coordinates into component parts.
  string[] startParts = GetRangeParts( start );
  string[] endParts = GetRangeParts( end );

  // Expand range between parts 
  //  (i.e. 1->3 becomes 1,2,3; A->C becomes A,B,C).
  int length = startParts.Length;
  int[] lengths = new int[length];
  string[][] expandedParts = new string[length][];
  for( int i = 0; i < length; ++i ) {
    expandedParts[i] = ExpandRangeParts( startParts[i], endParts[i] );
    lengths[i] = expandedParts[i].Length;
  }

  // Return all combinations of expanded parts.
  int[] indexes = new int[length];
  do {
      var sb = new StringBuilder( );
      for( int i = 0; i < length; ++i ) {
        int partIndex = indexes[i];
        sb.Append( expandedParts[i][partIndex] );
      }
      yield return sb.ToString( );
  } while( IncrementIndexes( indexes, lengths ) );
}

readonly Regex RangeRegex = new Regex( "([0-9]*)([A-Z]*)" );
string[] GetRangeParts( string range ) {
  // Match all alternating digit-letter components of coordinate.
  var matches = RangeRegex.Matches( range );
  var parts =
    from match in matches.Cast<Match>( )
    from matchGroup in match.Groups.Cast<Group>( ).Skip( 1 )
    let value = matchGroup.Value
    where value.Length > 0
    select value;
  return parts.ToArray( );
}

string[] ExpandRangeParts( string startPart, string endPart ) {
  int start, end;
  Func<int, string> toString;

  bool isNumeric = char.IsDigit( startPart, 0 );
  if ( isNumeric ) {
    // Parse regular integers directly.
    start = int.Parse( startPart );
    end = int.Parse( endPart );
    toString = ( i ) => i.ToString( );
  }
  else {
    // Convert alphabetic numbers to integers for expansion,
    //  then convert back for display.
    start = AlphaNumberToInt( startPart );
    end = AlphaNumberToInt( endPart );
    toString = IntToAlphaNumber;
  }

  int count = end - start + 1;
  return Enumerable.Range( start, count )
    .Select( toString )
    .Where( s => s.Length > 0 )
    .ToArray( );
}

bool IncrementIndexes( int[] indexes, int[] lengths ) {
  // Increment indexes from right to left (i.e. Arabic numeral order).
  bool carry = true;
  for( int i = lengths.Length; carry && i > 0; --i ) {
    int index = i - 1;
    int incrementedValue = (indexes[index] + 1) % lengths[index];
    indexes[index] = incrementedValue;
    carry = (incrementedValue == 0);
  }
  return !carry;
}

// Alphabetic numbers are 1-based (i.e. A = 1, AA = 11, etc, mod base-26).
const char AlphaDigitZero = (char)('A' - 1);
const int AlphaNumberBase = 'Z' - AlphaDigitZero + 1;
int AlphaNumberToInt( string number ) {
  int sum = 0;
  int place = 1;
  foreach( char c in number.Cast<char>( ).Reverse( ) ) {
    int digit = c - AlphaDigitZero;
    sum += digit * place;
    place *= AlphaNumberBase;
  }
  return sum;
}

string IntToAlphaNumber( int number ) {
  List<char> digits = new List<char>( );
  while( number > 0 ) {
    int digit = number % AlphaNumberBase;
    if ( digit == 0 )  // Compensate for 1-based alphabetic numbers.
      return "";

    char c = (char)(AlphaDigitZero + digit);
    digits.Add( c );
    number /= AlphaNumberBase;
  }

  digits.Reverse( );
  return new string( digits.ToArray( ) );
}

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

Если вы можете просто определить, что является преемником любой данной строки, то решение будет довольно простым. То есть, если у вас есть функция-преемник S для строк (например, с S('3A') = '4A'), то можно использовать что-то вроде следующего:

s = initial_string
while s != final_string do
  output s
  s = S(s)
output s

Что-то, что я использовал в прошлом для генерации всех строк заданной длины l и с заданным диапазоном символов от b до e, - это следующий фрагмент (псевдо) кода. Его можно легко адаптировать к широкому спектру вариаций.

// initialise s with b at every position
for i in [0..l) do
  s[i] = b
done = false
while not done do
  output s
  j = 0
  // if s[j] is e, reset it to b and "add carry"
  while j < l and s[j] == e do
    s[j] = b
    j = j + 1
    if j == l then
      done = true
  if not done then
    s[j] = s[j] + 1

Например, чтобы начать с определенной строки, вам нужно только изменить инициализацию. Чтобы установить конец, вам нужно только изменить поведение для внутреннего while, чтобы отдельно обрабатывать позицию l (ограничение до символа в конечной строке в этой позиции и, если достигнуто, уменьшение l).

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

Emperor XLII 06.09.2008 18:30

Другие вопросы по теме