Я удивлен, что никто не думал об использовании формы iter
с двумя аргументами :
from itertools import islice
def chunk(it, size):
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
Демо:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
Это работает с любой повторяемостью и лениво выдает результат Он возвращает кортежи, а не итераторы, но, тем не менее, он обладает определенной элегантностью. Это также не дополняет; если вы хотите заполнить, достаточно простого варианта выше:
from itertools import islice, chain, repeat
def chunk_pad(it, size, padval=None):
it = chain(iter(it), repeat(padval))
return iter(lambda: tuple(islice(it, size)), (padval,) * size)
Демонстрация:
>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
Как и решения на основе izip_longest
, выше всегда колодки. Насколько я знаю, не существует одно- или двухстрочного рецепта itertools для функции, которая опционально дополняет. Комбинируя два вышеупомянутых подхода, этот подход довольно близок:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
if padval == _no_padding:
it = iter(it)
sentinel = ()
else:
it = chain(iter(it), repeat(padval))
sentinel = (padval,) * size
return iter(lambda: tuple(islice(it, size)), sentinel)
Демонстрация:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]
Я считаю, что это самый короткий предложенный блок, который предлагает дополнительное заполнение.
Как заметил Томаш Гандор , два чанкера заполнения неожиданно остановятся, если они встретят длинную последовательность значений пэдов. Вот последний вариант, который разумным образом решает эту проблему:
_no_padding = object()
def chunk(it, size, padval=_no_padding):
it = iter(it)
chunker = iter(lambda: tuple(islice(it, size)), ())
if padval == _no_padding:
yield from chunker
else:
for ch in chunker:
yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))
Демонстрация:
>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
Это не так уж и сложно. Это кажется трудным, потому что есть много разных способов сделать это.
Попробуйте следующее:
<TabControl x:Name="documentArea"/>
Обработчик кнопки AddForm:
private void AddFormClick(object sender, RoutedEventArgs e)
{
object form = GetNewForm();
documentArea.Items.Add(form);
}
Вот и все. Вы должны реализовать GetNewForm ()
одним из двух способов. Он должен вернуть пользовательский элемент управления, который отображает форму.
ИЛИ еще лучше, пусть он вернет ваш документ, который вы хотите отобразить. Используйте шаблон данных
, чтобы выбрать элементы управления, которые будут использоваться для отображения этого документа. Этот метод будет более сложным в настройке.
Возможно, статья Джоша Смита о MVVM может дать вам представление о том, как разработать такой пользовательский интерфейс. Создаваемый пример представляет собой своего рода интерфейс документа с вкладками, поэтому вы можете использовать его в качестве начального блока.