Два решения FizzBuzz

Ниже приведены два решения проблемы FizzBuzz в Python. Какой из них более «Pythonic» и почему он более «Pythonic», чем другой?

Решение:

fizzbuzz = ''

start = int(input("Start Value:"))
end = int(input("End Value:"))

for i in range(start,end+1):
    if i%3 == 0:
        fizzbuzz += "fizz"
    if i%5 == 0:
        fizzbuzz += "buzz"
    if i%3 != 0 and i%5 != 0:
        fizzbuzz += str(i)

    fizzbuzz += ' '

print(fizzbuzz)

Решение второе:

fizzbuzz = []

start = int(input("Start Value:"))
end = int(input("End Value:"))

for i in range(start,end+1):
    entry = ''
    if i%3 == 0:
        entry += "fizz"
    if i%5 == 0:
        entry += "buzz"
    if i%3 != 0 and i%5 != 0:
        entry = i

    fizzbuzz.append(entry)

for i in fizzbuzz:
    print(i)
11 голосов | спросил Thomas Owens 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 01:06:54 +0300000000amMon, 14 Feb 2011 01:06:54 +030011 2011, 01:06:54

8 ответов


16

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

Всякий раз, когда вы обнаруживаете, что вы добавляете список внутри цикла for, вам стоит подумать, можно ли вместо этого использовать понимание списка. Перечисления списков не только более питоничны, но и быстрее.

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

def int_to_fizzbuzz(i):
    entry = ''
    if i%3 == 0:
        entry += "fizz"
    if i%5 == 0:
        entry += "buzz"
    if i%3 != 0 and i%5 != 0:
        entry = i
    return entry

fizzbuzz = [int_to_fizzbuzz(i) for i in range(start, end+1)]

Однако, пока мы находимся в этом, мы могли бы просто поместить всю логику fizzbuzz в функцию. Функция может принимать start и end как его аргумент и вернуть список. Таким образом, IO-логика, живущая вне функции, полностью отделена от логики fizzbuzz - также почти всегда хорошая идея с точки зрения дизайна.

И как только мы это сделали, мы можем поместить код ввода в блок if __name__ == "__main__":. Таким образом, ваш код может запускаться либо как сценарий в командной строке, который будет выполнять код ввода-вывода, либо загружаться как библиотека из другого файла python без выполнения кода ввода-вывода. Поэтому, если вам когда-либо понадобится писать графический интерфейс или веб-интерфейс для fizzbuzz, вы можете просто загрузить свою функцию fizzbuzz из файла, не меняя ничего. Повторное использование для победы!

def fizzbuzz(start, end):
    def int_to_fizzbuzz(i):
        entry = ''
        if i%3 == 0:
            entry += "fizz"
        if i%5 == 0:
            entry += "buzz"
        if i%3 != 0 and i%5 != 0:
            entry = i
        return entry

    return [int_to_fizzbuzz(i) for i in range(start, end+1)]

if __name__ == "__main__":
    start = int(input("Start Value:"))
    end = int(input("End Value:"))
    for i in fizzbuzz(start, end):
        print(i)

(Обратите внимание, что здесь я сделал int_to_fizzbuzz внутреннюю функцию, так как нет причин, по которым вы хотите вызвать ее за пределами функция fizzbuzz.)

ответил sepp2k 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 05:11:32 +0300000000amMon, 14 Feb 2011 05:11:32 +030011 2011, 05:11:32
4

Я прочитал хорошее решение с декоратором, и я думаю, что это путинский способ решения FizzBuzz:

@fizzbuzzness( (3, "fizz"), (5, "buzz") )
def f(n): return n

Генераторы также являются хорошим питоническим способом для получения списка чисел.

ответил nkint 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 14:24:12 +0300000000pmMon, 14 Feb 2011 14:24:12 +030011 2011, 14:24:12
2

Из моего краткого опыта работы с Python я бы сказал, что второй - более Pythonic, так как он использует списки Python, а первый просто добавляет к строкам, что приводит к тому, что результат будет немного уродливым и скомбинирован вместе. Хотя вы можете исключить всю дополнительную итерацию, добавив временную строку в первый цикл for, напечатав в конце цикла и сбросив значение, чтобы значения не нужно хранить для более чем одной итерации.

ответил Stephen Dedalus 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 01:20:15 +0300000000amMon, 14 Feb 2011 01:20:15 +030011 2011, 01:20:15
2

Я не уверен, что любое решение имеет какие-либо элементы «Pythonic». Я имею в виду, что вы не использовали никаких функций, характерных для Python. Я думаю, что для теста лакмуса для начинающих вы должны продемонстрировать свою способность создавать функции и использовать некоторые методы лямбда, карты, сокращения, фильтрации или списка. Поскольку форматирование вывода также является важным фундаментальным навыком, я бы бросил его на безвозмездное использование. Использование комментариев перед блоками кода и использование встроенных функций docstrings всегда является хорошей идеей. Использование 'else' итераторов также будет более похожим на python, но эта проблема не поддается такому решению.

Я бы не выбрал тип ввода на входе пользователя. Если вы используете Python 2.X, то избыточно вводить значение, поскольку оператор печати оценивает ввод. Если вы используете 3.X, то точка литья типа будет вынуждать значение от char к int. Проблема в том, что если вход включал буквенные символы, тогда Python выдавал бы ошибку. Кроме того, так как вы не проверяете границы, приведение в int не защитит вас от отрицательного целого числа, зависящего от вас.

Вот что я сделал бы в Python 2.x:

# gather start and end value from user
start = input("Start value: ")
end = input("End value: ")

def fizzbuzz(x):
    """ The FizzBuzz algorithm applied to any value x """
    if x % 3 == 0 and x % 5 == 0:
        return "FizzBuzz"
    elif x % 3 == 0:
        return "Fizz"
    elif x % 5 == 0:
        return "Buzz"
    else:
        return str(x)

# apply fizzbuzz function to all values in the range
for x in map(fizzbuzz, range(start,end+1)):
    print "{0:>8s}".format(x)
ответил typedeaf 26 Jam1000000amMon, 26 Jan 2015 09:37:01 +030015 2015, 09:37:01
1

Отвечая на мой собственный вопрос: «почему никто не использует yield?"

# the fizbuz logic, returns an iterator object that
# calculates one value at a time, not all ot them at once
def fiz(numbers):
    for i in numbers:
        if i % 15 == 0:
            yield 'fizbuz'
        elif i % 5 == 0:
            yield 'buz'
        elif i % 3 == 0:
            yield 'fiz'
        else:
            yield str(i)

# xrange evaluates lazily, good for big numbers
# matches well with the lazy-eval generator function
numbers = xrange(1,2**20)

# this gets one number, turns that one number into fuz, repeat
print ' '.join(fiz(numbers))

# returns: 1 2 fiz 4 buz fiz [...] fiz 1048573 1048574 fizbuz
  • четко отделяет логику fizbuz от конкатенации
  • является настолько простым и понятным, насколько возможно
  • итератор генератора не сохранить весь массив в памяти
  • , чтобы вы могли делать это на произвольных числах (см. проблема Эйлера № 10 )

То, что мне не нравится в этом решении, это три if s, тогда как проблема может быть решена двумя.

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

ответил Lorinc Nyitrai 9 J0000006Europe/Moscow 2016, 15:45:13
0

Нет никакой разницы в том, как «Pythonic» эти решения. Оба они вполне приемлемы. Если строка fizzbuzz становится очень длинной, предпочтительнее использовать список, так как вам не нужно делать копию строки на каждой итерации, но это очень незначительная проблема.

ответил Lennart Regebro 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 02:08:12 +0300000000amMon, 14 Feb 2011 02:08:12 +030011 2011, 02:08:12
0

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

Вместо этого я бы рекомендовал создать строку формата, а затем вставить элементы через форматирование строки, а затем добавить их в список fizzbuzz.

ответил 14 FebruaryEurope/MoscowbMon, 14 Feb 2011 02:15:18 +0300000000amMon, 14 Feb 2011 02:15:18 +030011 2011, 02:15:18
0

Не декоратор и не функция, это правильное решение. Сделайте несколько тестов, и вы увидите. Вызов функции в Python довольно дорог, поэтому старайтесь избегать их.

for n in xrange(1, 101):
    s = ""
    if n % 3 == 0:
        s += "Fizz"
    if n % 5 == 0:
        s += "Buzz"
    print s or n

ИЛИ

for n in xrange(1, 101):
    print("Fizz"*(n % 3 == 0) + "Buzz"*(n % 5 == 0) or n)
ответил Tolo Palmer 28 FebruaryEurope/MoscowbSat, 28 Feb 2015 02:14:14 +0300000000amSat, 28 Feb 2015 02:14:14 +030015 2015, 02:14:14

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132