【问题标题】:Python weirdness involving map and reduce涉及 map 和 reduce 的 Python 怪异
【发布时间】:2019-01-17 11:35:25
【问题描述】:

抱歉标题含糊不清,但我真的不知道这里发生了什么。

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)

期望的输出:

___
___
___

实际输出:

__
__
__

当我将 strxo 上的最终 else 更改为 return str(n) 而不是 return "_" 时,我得到:

000
000
000

这是我所期望的和我想要的形状,但我想替换那些零。这是什么原因造成的?

【问题讨论】:

  • 我不明白您如何期望从调用函数 3 次中得到 5 个下划线。或者为什么想要的输出首先是 5x3,而同时 3x3 的 0 网格是想要的形状。
  • 调试步骤 1:重写您的代码,使其可读。理想情况下,使用reduce 摆脱那种功能风格,而改用列表推导。但至少,将这个怪物分成多行。
  • 另外,你为什么要首先使用mapreduce?您的外部reduce 效率较低且难以理解'\n'.join,您的list(map(lambda…)) 效率较低且难以理解列表理解,而您的内部reduce...我不确定它应该是什么正在做;你为什么在这里调用strxo 的累计值?为什么这一切都必须是一条怪异的线,而不是把它分解成你可以理解和调试自己的小块?
  • @abarnert 我这样做是因为它是我习惯在 JavaScript 中使用的,很长一段时间后我又回到了 python,所以我只是想解决一个简单的问题-tac-toe 不用看太多。这篇文章上的每个人都非常有帮助!

标签: python arrays python-3.x functional-programming off-by-one


【解决方案1】:

问题是你最里面的reduce函数,作用于你的子列表的那个,总是把第二个参数变成_

lambda a, b: strxo(a) + strxo(b)

所以,在 reduce 的最后一个元素上,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

这是一个非常好的功能。函数式编程并不意味着“尽可能使用 map 和 reduce”。

所以,这里有一些很好的函数式编程:

joinstr = ''.join
join_newline = '\n'.join

def board_str(board):
    return join_newline(map(lambda l: joinstr(map(strxo,l)), board))

更好的是,您应该只使用 list comprehensions,它们是非常 功能构造(Python 从 Haskell 偷来的,顺便说一句)。它通常比map + lambda 更具可读性:

def board_string(board):
    return join_newline([joinstr(map(strxo, l)) for l in board])

【讨论】:

  • 它把 second 参数变成下划线?不是第一个
  • @Aran-Fey 它变成 both,这就是问题所在,所以它永远不会累积多个东西...
  • 对于懒惰的人,这里是可读、高效且正确的实现,没有任何功能性东西:print('\n'.join(''.join(strxo(n) for n in row) for row in arr))
  • @Aran-Fey 这是因为如果容器已经是列表或元组,则在实现中存在“快速路径”,否则,它无论如何都会调用列表!
  • @Aran-Fey 实际上,join 使用列表更快的原因是(在 CPython 中)join 只会将您提供的任何内容(除了 listtuple 转换为 @ 987654340@,所以可以直接迭代list-or-tuple内部存储数组。而且[…]list(…) 快(不像早期的 3.x 那样快,但仍然显着,其中一部分由于愚蠢的语义边缘情况而无法消除)。
猜你喜欢
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多