Извините за расплывчатое название, но я действительно понятия не имею, что здесь происходит.
from functools import reduce
arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
def strxo(n):
if (n == -1):
return "X"
elif (n == 1):
return "O"
else:
return "_"
def prboard(board):
print(reduce(lambda x, y: x + "\n" + y, list(map(lambda l: reduce(lambda a, b: strxo(a) + strxo(b), l), board))))
prboard(arr)
Желаемый результат:
___
___
___
Реальный выход:
__
__
__
И когда я меняю последний else
на strxo
на return str(n)
вместо return "_"
, я получаю:
000
000
000
Это то, чего я ожидал, и форму, которую я хочу, но я хочу заменить эти нули. Чем это вызвано?
Шаг 1 отладки: перепишите свой код, чтобы он был читабельным. В идеале избавьтесь от этого функционального стиля с помощью reduce
и используйте вместо него составные части списка. Но, по крайней мере, разделите это чудовище на несколько строк.
Кроме того, почему вы вообще делаете это с map
и reduce
? Ваш внешний reduce
просто менее эффективен и сложен для понимания '\n'.join
, ваш list(map(lambda…))
менее эффективен и сложнее для понимания понимания списка, а ваш внутренний reduce
… Я не уверен, что он должен делать; почему вы называете strxo
по накопленной стоимости здесь? И почему все это должно быть одной чудовищной строкой, а не разбивать ее на более мелкие части, которые вы можете понять и отладить самостоятельно?
@abarnert Я сделал это так, потому что это то, что я привык использовать в JavaScript, я возвращаюсь к python через долгое время, поэтому я просто пытался хэшировать простые крестики-нолики, не глядя слишком много вверх. Все участники этого поста были очень полезны!
Проблема в том, что ваша внутренняя функция сокращения, которая действует в ваших подсписках, всегда превращает второй аргумент в _
:
lambda a, b: strxo(a) + strxo(b)
Итак, в последнем элементе этого сокращения b
- это __
, который превращается в _
!
Вы хотите, чтобы сначала картаstrxo
на все, а потом уменьшил с помощью конкатенации.
Итак, вы хотите что-то вроде этого:
reduce(lambda x, y: x + "\n" + y, map(lambda l: reduce(lambda a, b: a + b, map(strxo, l)), board))
Обратите внимание, я удалил ненужный вызов list
.
Но что еще более важно, прекратите использовать reduce
и оператор конкатенации для соединения строк!
Это излишне многословно и неэффективно для загрузки (будет иметь квадратичную временную сложность).
Вместо этого используйте:
joinstr = ''.join
Это прекрасная функция. Функциональное программирование не означает «использовать карту и сокращать везде, где это возможно».
Итак, вот несколько хороших функциональных программ:
joinstr = ''.join
join_newline = '\n'.join
def board_str(board):
return join_newline(map(lambda l: joinstr(map(strxo,l)), board))
Еще лучше, вы должны просто использовать составить список, которые являются функциональными конструкциями в высшей степени (кстати, Python украл их из Haskell). Часто он читается лучше, чем map
+ lambda
:
def board_string(board):
return join_newline([joinstr(map(strxo, l)) for l in board])
Превращает аргумент второй в знак подчеркивания? Не тот первый?
@ Aran-Fey оказывается оба, в этом проблема, поэтому он никогда не накапливает несколько вещей ...
Для ленивых вот удобочитаемая, эффективная и правильная реализация без каких-либо этих функциональных вещей: print('\n'.join(''.join(strxo(n) for n in row) for row in arr))
.
@ Aran-Fey Я бы использовал понимание списка, это на самом деле быстрее на CPython, из-за некоторых деталей реализации, конечно :)
Я предпочитаю не ухудшать читаемость дополнительными парами скобок ... и надеюсь, что когда-нибудь в генераторах будет __length_hint__
(что, как я предполагаю, является причиной того, почему join
быстрее со списками) :(
Это здорово, спасибо! Я использовал все эти функции reduce и map, потому что это то, к чему я привык из JavaScript. Я тоже очень расстраивался из-за того, что все было нечитабельно. У Python действительно хорошо читаемый синтаксис, мне просто нужно его использовать!
@ Aran-Fey это потому, что в реализации есть "быстрый путь", если контейнер уже является списком или кортежем, в противном случае он все равно вызывает список!
кроме того, «Функциональное программирование не означает« использовать карту и сокращать везде, где это возможно »». Как будто ты меня лично знаешь: ')
@WilliamPapsco. Пифонический способ, вероятно, заключался бы в том, чтобы просто использовать тот лайнер, который показал Аран. Я просто демонстрировал функциональный стиль, который включает определение повторно используемых, ссылочно прозрачных функций. Сам Python не сильно ориентирован на функциональное программирование, он просто принимает хорошие функциональные конструкции, такие как понимание списков, когда они работают хорошо, но я бы сказал, что это очень императивно-ориентированный язык, если вы кодируете идиоматически
Технически я не уверен, что существует квадратичное поведение. Недавний CPython может иногда устранять его для конкатенации строк, а PyPy еще чаще, и оба они также имеют жестко запрограммированную оптимизацию для очень коротких строк. Но, тем не менее, «мог не будет квадратичным (но все равно будет на порядок медленнее, чем join
из-за чудовищно более высокого постоянного множителя)», вероятно, не является хорошей причиной для объединения строк, когда в этом нет необходимости. :)
@ Aran-Fey На самом деле причина, по которой join
быстрее со списками, заключается в том, что (в CPython) join
просто превращает все, что вы ему даете, кроме list
или tuple
, в list
, поэтому он может напрямую перебирать внутренний массив хранения списков или кортежей. И […]
быстрее, чем list(…)
(не так сильно, как в ранних 3.x, но все же значительно, и отчасти это невозможно устранить из-за глупых семантических краевых случаев).
Чувак, я понятия не имел, что S.join
работает таким образом! Какая полезная функция! Например: ";".join(["a", "b", "c"])
возвращает a;b;c
@WilliamPapsco Или даже ";".join('abc')
, потому что строки тоже можно повторять.
Я не понимаю, как вы ожидаете получить 5 знаков подчеркивания при трехкратном вызове функции. Или почему желаемый результат равен 5x3, в то время как сетка 3x3 из нулей является желаемой формой.