为啥我在 Python 中通过 reduce 对列表进行排序的代码会引发错误?

Posted

技术标签:

【中文标题】为啥我在 Python 中通过 reduce 对列表进行排序的代码会引发错误?【英文标题】:Why is my code for sorting a list via reduce in Python throwing an error?为什么我在 Python 中通过 reduce 对列表进行排序的代码会引发错误? 【发布时间】:2021-01-21 07:48:45 【问题描述】:

我想到了一种用 Python 对列表进行排序的单行器,但它一直没有奏效。我认为我可以使用reduce 来遍历列表并根据需要交换元素,就像在冒泡排序中一样。但是口译员今天很沮丧:

from functools import  reduce

def reduce_sort(*lst):
    sorter = lambda x, y: y, x if x > y else x, y
    return reduce(sorter, lst)

reduce_sort(5, 4, 6, 7, 1)

这是给我NameError: name 'x' is not defined

有人知道为什么会这样吗?

【问题讨论】:

我不太确定您希望这款分拣机如何工作…… 可能重复:***.com/questions/56045986/… 【参考方案1】:

sorter 是一个元组:

import ast

print(ast.dump(
  ast.parse("lambda x, y: y, x if x > y else x, y")
))

Try it online!

它实际上解析为:

Module(body=[Expr(value=
  Tuple(elts=[
    Lambda(
      args=arguments(posonlyargs=[], args=[arg(arg='x', annotation=None, type_comment=None), arg(arg='y', annotation=None, type_comment=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]),
      body=Name(id='y', ctx=Load())
    ),
    IfExp(
      test=Compare(left=Name(id='x', ctx=Load()), ops=[Gt()], comparators=[Name(id='y', ctx=Load())]),
      body=Name(id='x', ctx=Load()),
      orelse=Name(id='x', ctx=Load())
    ),
    Name(id='y', ctx=Load())
  ],
ctx=Load()))],type_ignores=[])

所以相当于

sorter = (lambda x, y: y), (x if x > y else x), y

...第二个表达式 x if x > y else x 将引发异常,因为它不知道 x 是什么。

您应该明确指定 if 表达式的计算结果为元组:

sorter = lambda x, y: (y, x) if x > y else (x, y)

现在sorter 是一个函数,但整个代码将不起作用,因为在reduce 的第二次迭代中,x 将成为一个元组(if 表达式返回的元组之一) '将与y 进行比较,后者是一个整数。这样的操作是不可能的,所以你会得到另一个错误,现在在运行时。

【讨论】:

Not that this actually works either though… @deceze,嗯,是的,因为x - 累加器 - 最终成为一个元组......我不确定reduce 无论如何是正确的工具 也许最好在此处包含 ast 命令,以防 TIO 明天停止工作。【参考方案2】:

functools.reduce 排序是个好主意。

为什么您的代码目前不起作用

但是,一旦您开始将初始列表中的两个或多个元素组合成一个排序的子列表,您的函数sorter = lambda x, y: y, x if x > y else x, y 就不再适用了:它只知道如何将两个元素组合成一个长度为 2 的列表;它不知道如何将两个排序列表组合成一个排序列表。

例子:

initial list =   [17, 45, 100, 96, 29, 15, 0, 32, 4, 100, 61, 11, 85, 96, 2, 75, 88, 51, 16, 27, 68, 74, 99, 27, 83, 38, 96, 40, 73, 99, 33, 36, 86, 54, 50, 36, 44, 56, 0, 62, 62, 87, 64, 14, 63, 78, 98, 64, 60, 57, 56, 80, 80, 32, 40, 51, 64, 29, 21, 43, 63, 44, 35, 25, 37, 10, 13, 9, 34, 10, 39, 70, 14, 33, 49, 16, 80, 50, 14, 82, 51, 82, 67, 10, 20, 6, 59, 5, 31, 62, 83, 92, 13, 59, 71, 65, 1, 25, 78, 45]
began reducing = [[17, 45], [96, 100], [15, 29], [0, 32], 4, 100, 61, 11, 85, 96, 2, 75, 88, 51, 16, 27, 68, 74, 99, 27, 83, 38, 96, 40, 73, 99, 33, 36, 86, 54, 50, 36, 44, 56, 0, 62, 62, 87, 64, 14, 63, 78, 98, 64, 60, 57, 56, 80, 80, 32, 40, 51, 64, 29, 21, 43, 63, 44, 35, 25, 37, 10, 13, 9, 34, 10, 39, 70, 14, 33, 49, 16, 80, 50, 14, 82, 51, 82, 67, 10, 20, 6, 59, 5, 31, 62, 83, 92, 13, 59, 71, 65, 1, 25, 78, 45]
sorter([15, 29], [0, 32]) = ???

如何修复这个sorter 函数?我们想要的是能够将两个排序列表合并为一个排序列表。这会敲响警钟吗?是的!这是来自mergesortmerge 函数。

我们可以自己实现它......或者如果它还没有在某个地方实现,我们可以查看 python 标准模块。它是。 It's called heapq.merge.

sorter 替换为heapq.merge

让我们用heapq.merge替换你的sorter

import random
import functools
import heapq
ll = [random.randint(0,100) for i in range(100)]
list(functools.reduce(heapq.merge, ll))
# TypeError: 'int' object is not iterable

哎呀! heapq.merge 需要两个迭代。最初我们的列表不包含列表,它包含单个元素。我们需要将这些单个元素替换为单例列表或单例元组:

import random
import functools
import heapq
ll = [(random.randint(0,100),) for i in range(100)]
list(functools.reduce(heapq.merge, ll))
# [3, 3, 4, 5, 5, 5, 7, 8, 8, 10, 11, 13, 15, 15, 15, 15, 16, 16, 17, 18, 19, 20, 20, 20, 24, 25, 25, 27, 27, 28, 30, 30, 30, 30, 31, 32, 33, 33, 38, 38, 39, 39, 41, 42, 43, 43, 43, 46, 46, 47, 49, 50, 51, 52, 53, 55, 62, 63, 63, 64, 64, 64, 65, 66, 67, 68, 69, 71, 72, 72, 72, 72, 73, 73, 73, 74, 78, 79, 82, 82, 83, 83, 86, 86, 86, 86, 89, 91, 92, 93, 95, 96, 96, 96, 97, 98, 99, 99, 100, 100]

有效!请注意 ( ,) 内部的 ll 构造,它生成单例元组。如果你不喜欢元组并且更喜欢列表,你可以使用[ ] 而不是( ,)

当然,您希望能够对包含数字而不是单例元组的列表进行排序。因此,让我们将 lambda x: (x,)lambda x: [x] 添加到我们的组合中:

最终解决方案

import functools
import heapq
def reduce_sort(ll):
  list(functools.reduce(heapq.merge, map(lambda x: (x,), ll)))

import random
ll = [random.randint(0,100) for i in range(100)]
reduce_sort(ll)
# [3, 3, 4, 5, 5, 5, 7, 8, 8, 10, 11, 13, 15, 15, 15, 15, 16, 16, 17, 18, 19, 20, 20, 20, 24, 25, 25, 27, 27, 28, 30, 30, 30, 30, 31, 32, 33, 33, 38, 38, 39, 39, 41, 42, 43, 43, 43, 46, 46, 47, 49, 50, 51, 52, 53, 55, 62, 63, 63, 64, 64, 64, 65, 66, 67, 68, 69, 71, 72, 72, 72, 72, 73, 73, 73, 74, 78, 79, 82, 82, 83, 83, 86, 86, 86, 86, 89, 91, 92, 93, 95, 96, 96, 96, 97, 98, 99, 99, 100, 100]

【讨论】:

以上是关于为啥我在 Python 中通过 reduce 对列表进行排序的代码会引发错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在 Spring-boot 应用程序中通过 SessionFactory 有循环依赖?

Django:为啥当我在 django 中通过 popen 使用 Ghostscript 时会出现“找不到文件”错误

ReactJS:如何在 Jest 中通过动作触发减速器

在 Python 中通过 Header 获取主机公共 IP

为啥不调用 WebBrowser 控件中通过 setTimeout 方法调度的代码

为啥在 PHP 中通过引用传递?