Мне было интересно, есть ли библиотека или алгоритм с открытым исходным кодом, который может расширить нечисловой диапазон. Например, если у вас 1A до 9A, вы должны получить
1A, 2A, 3A, 4A, 5A, 6A, 7A, 8A, 9A.
Я пробовал использовать Google для этого, и лучшее, что я мог придумать, было Regex, которое расширяло числа с помощью тире (1-3, становясь 1,2,3).





Я пытался оставить его в некотором роде открытым, потому что количество возможностей ошеломляет. Я считаю, что это один из тех вопросов, на который нельзя ответить на 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как одно число). Я все еще думаю, что проблема многомерная, но, думаю, нам остается только дождаться разъяснений автора :)