在Python列表中找到高于特定阈值的最小值

玛拉

我正在尝试在列表中找到某个阈值以上的最小值,而没有使用min()函数(我正在努力进行练习)。

我已经设法通过首先创建一个高于阈值的值列表,然后遍历该列表,将值保存到变量(如果它小于先前看到的值)来进行管理:

def minpass(mymarks, mypass):
    passed= [x for x in mymarks if x >= mypass]
    min_value = passed[0]
    for x in passed: 
        if x < min_value:
            min_value = x
    return min_value

x = [2, 53, 90]
y = 50

minpass(x, y)

这将正确返回53。

是否可以在不创建第二个列表(通过)的情况下执行此操作?为什么添加第二个条件不起作用?例如

def minpass(mymarks, mypass):
    min_value = mymarks[0]
    for x in mymarks: 
        if x < min_value and x >= mypass:
            min_value = x
    return min_value

x = [2, 53, 90]
y = 50

minpass(x, y)

这将错误地返回2而不是53。

阿巴内特

由于您是作为学习经验来做的,因此:

为了避免创建第二个列表,最有趣的替代方法是创建一个惰性迭代器。在幕后,这将根据需要计算出下一个过滤后的值,而不是预先构建它们的列表。但是从您的许多代码来看,它实际上看起来就像您创建了一个列表一样。

创建延迟迭代器的方式有多种-显式迭代器类,filter内置函数,生成器函数-但在您的情况下,您可以只使用生成器表达式来代替列表理解:

passed = (x for x in mymarks if x >= mypass)

我要做的就是将方括号更改为括号,然后您神奇地得到了一个懒惰的迭代器。

但是,迭代器只能用于一次按顺序遍历值。您无法执行索引(passed[0])之类的操作。因此,您需要重新考虑一下代码。但这很简单:

def minpass(mymarks, mypass):
    passed = (x for x in mymarks if x >= mypass)
    min_value = next(passed) # this consumes the first value
    for x in passed: # this loops over all the remaining values
        if x < min_value:
            min_value = x
    return min_value

在我们讨论它的时候,您可能需要考虑将代码重构为两个函数-编写自己的minvalue函数,该函数需要任何可迭代的对象(可迭代对象是一个惰性迭代器,或者一个序列(如列表),或者可以在for循环)并返回最小值:

def minvalue(it):
    it = iter(it) # this makes sure we have a lazy iterator
    min_value = next(it) # this consumes the first value
    for x in it: # this loops over all the remaining values
        if x < min_value:
            min_value = x
    return min_value

def minpass(mymarks, mypass):
    return minvalue(x for x in mymarks if x >= mypass)

或者也许进一步重构:

def passvalues(it, mypass):
    return (x for x in it if x >= mypass)

def minpass(mymarks, mypass):
    return minvalue(passvalues(mymarks, mypass))

请注意,这种方法可以自动解决您的第二个问题。您的问题是mymarks[0]可能不是>= mypass要改写工作,您必须执行以下操作:

def minpass(mymarks, mypass):
    for x in mymarks:
        if x >= mypass:
            min_value = x
            break
    for x in mymarks: 
        if x < min_value and x >= mypass:
            min_value = x
    return min_value

但是将事情写成一连串的迭代器转换会迫使您按顺序排列它们(首先进行过滤,然后进行最小查找),这意味着您将自动获取第一个过滤后的值而不是第一个值,同时仍然交错您想要的方式来工作(并避免创建整个不必要列表的时间和空间成本)。


如果您想对这些想法进行更深入的介绍,David Beazley的《面向系统程序员生成器技巧》就是很棒的。


最后要考虑的问题:是否有办法摆脱对第一个价值的特殊对待?

您可以从大于任何值的值开始,也可以使用标志指定到目前为止是否找到了最小值:

def minvalue(it):
    found_min = False
    for x in it:
        if not found_min or x < min_value:
            min_value = x
            found_min = True
    return min_value

这样做的优点(或可能是缺点,取决于您想要的...)在传递一个空列表时仍然会失败,但是它简化了循环(无需提取第一个值,这意味着无需调用iter)。但是,手动标记管理可能会添加更多的杂音,而不是帮助您删除。尽管如此,还是值得比较两者并自己决定。


您可能需要考虑自己尝试的其他事项:

  • 重写minvalue左右reduce
  • 重写minvalue以使用“小于所有”值。
    • float('inf') 如果所有值均为整数或浮点数,则将起作用。
    • 如果值完全可以是任何值,则可以使用自定义方法定义一个BiggestThing,然后使用__lt__BiggestThing()
  • 找出所有不同的选项,以了解如何使用空输入(或不为空但没有任何可通过过滤器的值的输入)以及如何实现它们。
  • 尝试使用已排序的数据结构,就像一个heapq要执行的操作一样minvalue-然后可以展开它以返回两个最低值而不是仅返回最低值,或者采用一个numlowest参数并返回那么多个。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章