Вертикально распечатать список строк на стандартный вывод в 3 колонки с длиной колонки максимально сбалансированным


Это был вопрос интервью я боролся с, я отправляю мое решение в надежде выглядеть более элегантный/эффективные решения.

Проблема: дан список строк, то печатать ее элемент в стандартный вывод по одному вертикально в 3 колонки, а убедившись, что длина в 3 колонки являются максимально сбалансированным.

Например, дается список = ['А','Б','С','Д','Е','е','г','ч','я','Дж']

Выход может быть либо:

a e h
b f i
c g j
d

Или

a d g
b e h
c f i
    j

Но нет:

a e i
b f j
c g 
d h  

Мой подход в Python 3:

В основном я поместил каждое слово в "в N 3" в 2D список по горизонтали и попытаться распечатать их вертикально. Если длина не кратна 3, я увеличиваю вручную длина первых 2-х строк в зависимости от остатка. Это создает немало крайние случаи для меня, чтобы справиться по отдельности, например, когда длина списка-это между 3 и 6, или если длина меньше или равна 3.

def assign_words(grid, words):
    i = 0
    for row in range(len(grid)):
        for col in range(len(grid[row])):
            grid[row][col] = words[i]
            i += 1
            if i == len(words):
                return

def print_words(words):
    word_count = len(words)
    rows = word_count // 3
    extra_columns = word_count % 3

    grid = [[''] * 3 for _ in range(rows)]

    # special case
    if word_count <= 3:
        grid.append(['']*3)
        assign_words(grid, words)
        for row in grid:
            print (' '.join(row))
        return

    if extra_columns == 1:
        if rows > 2:
            grid[0].append('')
        elif rows == 2:
            grid[1].pop()
            grid.append(['']*3)
    elif extra_columns == 2:
        if rows > 2:
            grid[0].append('')
            grid[1].append('')
        else:
            grid.append(['']*3)

    assign_words(grid, words)

    # special case
    if 3 < word_count < 6:
        print (grid[0][0]+ ' '+grid[0][2]+' '+grid[1][1])
        print (grid[0][1]+ ' '+grid[1][0]+' '+grid[1][2])
        return

    # print grid
    for col in range(len(grid[0])):
        for row in range(len(grid)):
            if col == len(grid[row]):
                break
            print (grid[row][col], end=' ')
        print ()
 print_words(['a','b','c','d','e','f','g'])


3935
10
задан 26 февраля 2018 в 05:02 Источник Поделиться
Комментарии
3 ответа

Вы должны взглянуть в Python итерации расширенные возможности, т. е. в itertools модуль. Вы найдете множество рецептов, среди которых take:

import itertools

def take(n, iterable):
"Return first n items of the iterable as a list"
return list(itertools.islice(iterable, n))

Которая поможет вам извлечь столбцы из своего списка слов. Вы можете разделить длину списка на 3, чтобы узнать, сколько данных в каждом столбце, и регулировать с помощью остатка от деления длины на 3 (по модулю первые столбцы вам выбрать одно слово); которые могут быть облегчены благодаря divmod. Наконец, вам нужно организовать ваши столбцы в строки, которые легко сделать с помощью itertools.zip_longest.

Что делается, необходимо, чтобы напечатать данные. Самым простым решением будет только распечатать их, как это делаете вы:

def print_words(words):
columns, dangling = divmod(len(words), 3)
iterator = iter(words)
columns = [take(columns + (dangling > i), iterator) for i in range(3)]
for row in itertools.zip_longest(*columns, fillvalue=''):
print('{} {} {}'.format(*row))

Который ведет себя вот так:

>>> print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
a e h
b ffffffff i
cccccc gg j
dd


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

def print_words(words):
columns, dangling = divmod(len(words), 3)
iterator = iter(words)
columns = [take(columns + (dangling > i), iterator) for i in range(3)]
paddings = [max(map(len, column)) for column in columns]
for row in itertools.zip_longest(*columns, fillvalue=''):
print(' '.join(word.rjust(pad) for word, pad in zip(row, paddings)))

Которые ведут себя более вежливо:

>>> print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
a e h
b ffffffff i
ccccccc gg j
dd


Наконец, мы можем обобщить, чтобы поддерживать произвольное количество столбцов:

def print_words(words, column_count=3):
columns, dangling = divmod(len(words), column_count)
iterator = iter(words)
columns = [take(columns + (dangling > i), iterator) for i in range(column_count)]
paddings = [max(map(len, column)) for column in columns]
for row in itertools.zip_longest(*columns, fillvalue=''):
print(' '.join(word.rjust(pad) for word, pad in zip(row, paddings)))

Что позволяет

>>> print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
a e h
b ffffffff i
ccccccc gg j
dd
>>> print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'], 4)
a dd gg i
b e h j
ccccccc ffffffff
>>> print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'], 5)
a ccccccc e gg i
b dd ffffffff h j

10
ответ дан 26 февраля 2018 в 10:02 Источник Поделиться

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

алгоритм

Вы предлагаете несколько открытых спецификаций, заметив, что мы могли бы напечатать это или , что в равной мере правильно. Это нормально. Но вам необходимо заколачивать детали, прежде чем остановиться на конкретном алгоритме. Так произвольно выбрать один. Я рекомендую первый вариант выхода, так как он позволяет переборе самым естественным образом.

Вы говорите, что вам нужен 2-Д структуры данных, предлагая произвольного доступа, для того, чтобы поставить правильный вывод. Я не понимаю, зачем это надо. Палка с простыми, если вы можете.

Вариант 1

Создать массив из 3 текущие индексы первоначально указывая на 'А', 'Е', & 'ч'. Создать массив индексов 3 'конец' указывает на 'Е', 'Н', И мимо конца массива.

Для каждой строки вывода, для каждого столбца выходной, если ток меньше, чем концов, выделяет текущее слово и инкремент индекса.

вариант 2

За счет некоторого дополнительного хранения мы можем еще более упростить.

Вычислить stride а примерно на треть длины wordsи инициализировать done = set().

Для каждой строки вывода iнапротив, каждый столбец вывода на шаг, так что вы условно выходной word[j]. Добавить j для done. Подавить вывод при j in done.

вариант 3

Не используйте для хранения. Существует три случая, в зависимости от количества слов мод 3 равен 0, 1 или 2. Просто закодировать его ifS, или использовать div & мод выражений.

Первые два варианта ориентированы на, что делает его легко проверить, что строка 0-это правильно выход, и что переход на каждый следующий ряд четко отрегулировано правильно. Третий вариант лучше, за счет простоты тестирования.

код

def assign_words(grid, words):

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

        grid[row][col] = words[i]

Используя столбцам привело бы к более естественному сетки[х][г] или сетки[колонка][строка] нотации.

def print_words(words):

Пожалуйста, добавьте немного подпись:

def print_words(words, k=3):

В магическое число 3 в выражениях типа // 3 и % 3 становится параметр k.

# special case
if word_count <= 3:
grid.append(['']*3)

Это предполагает, что ваш rows это назначение было неудачным. Этот случай больше нет "специального" если вы назначите:

rows = (word_count + 2) // 3

(или (word_count + k - 1) // k)

Последующее сравнение rows С 2 также, кажется, безвозмездно "специальное", что делает его трудно поверить, что они верны для всех входов.

# special case
if 3 < word_count < 6:
print (grid[0][0]+ ' '+grid[0][2]+' '+grid[1][1])
print (grid[0][1]+ ' '+grid[1][0]+' '+grid[1][2])

Продвижение слова в новые строки в том, что способ предполагает первоначальное задание был неисправен. Без письменного спецификаций, без строкой документации функции назначение, трудно взвалить вину и сложно предположить, что средство правовой защиты. Напишите комментарий, и объявить глючный код, если он не соответствует комментарию.

# print grid

Хорошо, эти вложенные циклы четкие, прозрачные код.

        print (grid[row][col], end=' ')

Используя .ljust() здесь позволит вам вместить слова переменной ширины.

6
ответ дан 26 февраля 2018 в 07:02 Источник Поделиться

Вот моя попытка найти простое и лаконичное решение.


  • Он использует функцию, чтобы вычислить высоту каждого столбца.

  • Это создает прямоугольную таблицу (список списков), с d столбцов и столько строк, сколько необходимо (в height в первой колонке).

  • Он перебирает каждый столбец и строку и заполняет символов.


l = ['a','b','c','d','e','f','g','h','i','j']

def height(n, d, i=0):
"""Return the height of the i-th column, for n elements in d columns."""
rows, longer_columns = divmod(n, d)
return range(rows + (i < longer_columns))

def distribute_elements(words, d=3):
'''Distribute words into a table of d columns as evenly as possible.'''
n = len(words)
table = [[''] * d for _ in height(n, d)]
word = iter(words)
for i in range(d):
for j in height(n, d, i):
table[j][i] = next(word)
return table

for rows in distribute_elements(l):
print(' '.join(rows))

Он выводит:

a e h
b f i
c g j
d

0
ответ дан 27 февраля 2018 в 02:02 Источник Поделиться