【问题标题】:Passing function to the key of a sort function将函数传递给排序函数的键
【发布时间】:2017-07-14 12:29:11
【问题描述】:

我有一个清单

lis = [ [0, 1], [1, -1], [1, 0] ]

我想根据特定条件对其进行排序。我想使用一个逻辑,只要lis[i][0] 等于lis[i + 1][0],就交换元素。类似的东西

在上面的列表中,第二个和第三个元素是[1, -1] 和[1, 0],其中lis[i][0] == lis[i + 1][0]。所以我会交换它,这样我的新列表就变成了

lis = [ [0, 1], [1, 0], [1, -1] ]

这是我的功能:

def sortList(lis3):
    for i in range(0, len(lis3) - 1):
        for j in range(i + 1, len(lis3)):
            if lis3[i][0] == lis3[j][0]:
                lis3[i], lis3[j] = lis3[j], lis3[i]

我想将此函数传递给列表的sort 方法,使其按照以下逻辑排序:

我试过这样做,但没有用:

lis.sort(key=sortList)

如何使这个函数在sort 方法中工作?

【问题讨论】:

  • 这与排序不同...排序必须是全局属性并且不直接依赖于邻居。 (这将如何与合并排序或其他排序算法一起工作?结果如何相同?python uses [timsort ])。
  • @hiroprotagonist 那么我可以用其他方法吗?
  • 问题没有很好的定义...lst = [(0,0), (0,1), (0,2)]:结果如何?您对列表的迭代次数和次数是多少?你什么时候做完?

标签: python list python-3.x sorting


【解决方案1】:

您无法使用list.sort 或sorted 使其可靠地工作。问题是这些函数背后的排序算法并不能保证比较哪些元素(它只是说结果将被排序)。要使list.sort 或sorted 可靠地工作,您需要total ordering relation 的key,这是您的函数不提供的。

另外,key 应该是一个将一个元素转换为“应该比较的属性”的函数,它确实将列表的每个元素传递给 key 函数而不是整个列表(实际上在 CPython 中当你sort它时列表是空的,所以它根本无法工作)。

【讨论】:

    【解决方案2】:

    正如在其他答案中已经说过的那样,不可能(或至少不重要)使其与sorted 一起工作。但是,您可以不进行任何排序,只需根据第一个元素收集索引即可:

    from collections import defaultdict
    
    lis = [[0, 1], [1, -1], [1, 0]]
    
    d_idx = defaultdict(list)
    
    for idx, item in enumerate(lis):
        d_idx[item[0]].append(idx)
    

    然后创建一个“结果”列表并反转具有相同第一个元素的所有元素的索引:

    res = [None]*len(lis)
    
    for _, value in d_idx.items():
        for orig_idx, target_idx in zip(value, reversed(value)):
            res[target_idx] = lis[orig_idx]
    

    这给出了res 的:

    >>> res
    [[0, 1], [1, 0], [1, -1]]
    

    注意:“反转”具有相同第一个元素的元素可能不是所需的行为。因为作为@hiro protagonist noted in the comments:

    问题并没有很好地定义...lst = [(0,0), (0,1), (0,2)]:结果如何?您对列表的迭代次数和次数是多少?你什么时候做完?

    如果您需要不同的行为,应将哪些元素分配到您(可能)只需要更改for orig_idx, target_idx in zip(value, reversed(value)): 行并在那里应用您想要的操作。


    另一个优点是这种方法仅具有 O(n) 运行时行为,而 sort 具有 O(n*log(n))(平均)运行时。所以它可能会更快。

    【讨论】:

    • 酷!所以我最好不要使用排序功能并尝试按照自己的方式进行操作:)
    【解决方案3】:

    执行此排序的一种方法是对sorted 使用cmp 参数(在Python 3 中使用functools.cmp_to_key)并在两个子列表的索引0 处的项目比较相等或为零时返回-1。这假设项目是成对的并且是连续的,所以它并不是一种详尽的排序,只是一种交换项目的黑客方式:

    lis = [ [0, 1], [1, -1], [1, 0], [2, 5], [2, 6]]
    
    print sorted(lis, cmp=lambda x, y: -1 if x[0]==y[0] else 0)
    # [[0, 1], [1, 0], [1, -1], [2, 6], [2, 5]]
    

    但是,当两个以上的项目在索引 0 处具有相同的值或项目不连续时,排序会变得不明确。


    OTOH,您可以遍历您的列表并交换符合您条件的成对连续项目。无需申请排序!

    【讨论】:

    • 并且不能保证所有项目都相互比较。因此,它可能有效或无效,具体取决于比较的项目。
    • @Moses 你能在 python 3 中显示相同的实现吗?我尝试使用 functools.cmp_to_key 但弹出错误。
    • @SouvikRay sorted(lis, key=functools.cmp_to_key(lambda x, y: -1 if x[0]==y[0] else 0))
    • @SouvikRay 记住 import functools 并查看其他答案。
    【解决方案4】:

    python sort 方法将在适当的位置对列表进行排序,仅使用

    但是,您的规则并未定义任何可以用a < b 描述元素a、b 的关系。它只定义了相邻元素之间的关系,它甚至不是一个稳定的关系:如果a < b 所以你交换了这两个项目,那么它立即变成b < a 的情况。

    或者简而言之,lis.sort() 完全不适合这项任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-12
      • 2021-05-08
      • 1970-01-01
      • 2014-05-25
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多