繁体   English   中英

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

[英]Find minimum value above a certain threshold in a Python list

我试图在列表中找到最小值,超过某个阈值而不使用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 around reduce
  • 重写minvalue以使用“小于任何”值。
    • 如果所有值都是整数或浮点数,则float('inf')将起作用。
    • 如果值可以是任何值,则可以使用自定义__lt__方法定义BiggestThing类,并使用BiggestThing()
  • 找出如何处理空输入的所有不同选项 - 或者非空的输入但没有任何通过过滤器的值 - 以及如何实现它们。
  • 尝试使用排序数据结构,例如heapq数据结构来执行minvalue - 然后您可以将其展开以返回两个最低值而不是最低值,或者获取numlowest参数并返回那么多。

通常,使用循环查找min (或max )要求您将返回变量初始化为巨大的(或巨大的负值)。

在您的情况下,您正在初始化列表中第一个元素的最小值。 然后对于后续元素, x < min_value检查将评估为False因为该值已经是整个列表的最小值。

以下是修改代码的一种方法:

def minpass(mymarks, mypass):
    min_value = 1e10  # something bigger than your maximum value
    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)
#53

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM