То, что вы ищете, это декартово произведение . По сути, это дает все возможные комбинации двух входов списка. К счастью, библиотека Python itertools
имеет встроенный класс product
, который может выступать в качестве итератора. Итак, теперь мы хотим сделать это в 3 шага:
[1110 ] 1. Разобрать строку
Поскольку я предполагаю, что вы можете иметь любое количество {x:y}
с, мы будем использовать цикл while
def parse_string(string):
index_of_ = string.find ("_")
name = string [:index_of_]
if index_of_ == -1: return name, []
ranges = []
while index_of_ != -1: # still an _
next_underscore = string.find ("_", index_of_ + 1)
if next_underscore == -1: # last underscore
range_ = string [index_of_ + 1:]
else: range_ = string [index_of_ + 1:next_underscore]
start = range_ [1 : range_.find (":")]
end = range_ [range_.find (":") + 1 : -1]
ranges.append (tuple (range (int (start), int (end) + 1))) # put all those numbers in
if next_underscore == -1: break
else: index_of_ = next_underscore
return name, ranges
Эта функция зацикливает строку, извлекает имя и итеративно находит диапазоны. Он возвращает имя и список всех диапазонов, поэтому для вашего примера - NAME, [(1, 2), (3, 4, 5)]
Теперь, когда у нас есть список всех диапазонов, давайте получим декартово произведение этого списка.
from itertools import product def generate_combos(ranges): return product (*ranges) # unpack the list
Это просто помогает нам передать список в product
, который выполняет всю тяжелую работу.
Здесь нам нужна функция для объединения двух других. Сначала он анализирует строку, чтобы получить диапазоны и имя. Затем для каждой комбинации декартового произведения он итеративно добавляет цифру к имени, а затем сохраняет результат:
def generate_string(string):
name, ranges = parse_string (string)
results = []
for combo in generate_combos (ranges):
result = name
for num in combo:
result += f"_{num}"
results.append (result)
return results
Пример:
print (generate_string ("NAME_{1:2}_{3:5}"))
Дает:
['NAME_1_3', 'NAME_1_4', 'NAME_1_5', 'NAME_2_3', 'NAME_2_4', 'NAME_2_5']
When you call reserve
, you're making a request to change the capacity. Implementations will only guarantee that a number equal to or greater than this amount is reserved. Therefore, a request to shrink capacity may be safely ignored by a particular implementation.
However, I encourage you to consider whether this isn't premature optimization. Are you sure that you're really making so many strings that it's a memory bottleneck for you? Are you sure that it's actually memory that's the bottleneck?
From the documentation for reserve
:
This can expand or shrink the size of the storage space in the string, although notice that the resulting capacity after a call to this function не обязательно равен
res_arg
но может быть как равным, так и большим чемres_arg
, поэтому сокращается запросы могут или не могут произвести фактическое сокращение выделенных пространство в определенной библиотеке реализация. В любом случае, это никогда обрезает содержимое строки (для этого в целях изменения размера или очистки, изменить содержание).
Изложение ответа Навина :
string x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52
// This tends not to work (although in theory it could):
//x = "XYZ";
//cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52
// This tends to work (although in theory it might not):
string("XYZ").swap(x);
cerr << x.capacity() << "\n"; // MSVC++: 15 g++: 3
Обратите внимание, что если базовый распределитель выделяет больше, чем n байт при построении строки
длины n (например, путем округления до ближайшего 32, как это делает MSVC ++), нет способа сделать он использует меньше байтов . Но вы, вероятно, не захотите делать это в любом случае, так как это «округление» сделано для того, чтобы сделать процесс динамического выделения памяти более эффективным, а также побочным эффектом ускорения конкатенации коротких строк в среднем (поскольку требуется меньше перераспределений). произойти).
Я думаю, что вы можете использовать метод подкачки для освобождения данных. поменяйте местами пустую локальную строку, чтобы, когда локальная строка вышла из области видимости, память была освобождена.
Try the std::string
swap-trick to shrink your strings:
std::string( str.data(), str.size() ).swap( str )
Where str
is the string you want to cut down to size.
В основном это зависит от реализации. Идея состоит в том, чтобы минимизировать запросы выделения и фрагментации памяти. Легко доказать, что, удваивая существующий размер при каждом расширении блока, количество выделений и фрагментация памяти сводятся к минимуму. Поэтому, как правило, реализации контейнера STL удваивают существующий блок при расширении.
Одна вещь, которую вы можете сделать, это использовать собственный распределитель, который не будет выделять больше, чем необходимо, затем создайте свой std ::
емкость НИКОГДА не будет меньше 15 с dinkumware STL. std :: basic_string имеет объединение, которое является указателем на выделенный буфер или 16-байтовый буфер (если Capacity () <= 15) (для строковых символов)
, смотрите файл заголовка xstring
в приведенном вами примере где вы резервируете 16, вы фактически резервируете 17 (один для нуля), который> 16, поэтому он размещается, а не кэшируется в 16 байтах в объединении указателей кэша. Это распределение удваивает предыдущий размер (16), так что вы получите 32. Емкость этой строки тогда, вероятно, равна 31.
Но это зависит от реализации STL.
Изменение параметра шаблона распределителя в шаблоне decl из std :: basic_string НЕ достаточно - выбор того, когда выделять и сколько, находится в std :: basic_string; алгоритм роста НЕ в распределителе. Удвоение предыдущего размера (и сокращение при <
Не существует гарантированной минимальной емкости для std :: string
. Вы можете запросить любую емкость, которую хотите, вызвав резерв
, но конкретная реализация гарантирует только установку емкости на некоторое количество, большее или равное запрошенному размеру.
Вот модифицированная версия вашей программы, которая тестирует несколько методы сжатия строк:
#include <string>
#include <iostream>
using namespace ::std;
template< typename S >
S & reserve_request( S & s, typename S::size_type n ) {
s.reserve( n ); return s;
}
template< typename S >
S & shrink_request1( S & s ) { s.reserve(); return s; }
template< typename S >
S & shrink_request2( S & s ) { S( s ).swap( s ); return s; }
template< typename S >
S & shrink_request3( S & s ) { S( s.c_str() ).swap( s ); return s; }
template< typename S >
void test( S & s ) { cout << s.capacity() << endl; }
int main() {
string temp = "1234567890123456"; // length 16
string str;
test( str ); // 15
test( reserve_request( str, 16 ) ); // 31
test( str += temp ); // 31
test( reserve_request( str, 16 ) ); // 31
test( shrink_request1( str ) ); // 31
test( shrink_request2( str ) ); // 31
test( shrink_request3( str ) ); // 31
return 0;
}
Может показаться, что std :: string
Visual C ++ обычно сохраняет некоторую свободную емкость.
Если ваш проект загружает большое количество строк, считанных из внешнего источника, размер которого затем никогда не меняется, вам может быть лучше (как другие предлагали) хранить их в одном большом блоке символьной памяти, разделенном символами '\ 0'
(то есть в виде C-строк). Если хочешь, Вы можете предоставить функции-оболочки, которые возвращают std :: string
s на лету.