Python Lambda计数/循环函数

时间:2012-04-14 16:34:53

标签: python lambda

如果这是其他地方的问题,我很抱歉。通过谷歌和Stackforum搜索我没有找到任何可以推断出答案的内容;但我觉得其中的一部分就是我。

我正在尝试将lambda作为一个概念,作为其中的一部分,我正在寻找使用它的方法。

所以,如果从功能的角度来看这是一个非常愚蠢的关于lambda的事情,请随时告诉我并解释。但无论哪种方式,我仍然想知道答案/仍然想知道如何使用python语言。

因此,出于测试目的,我有:

my_test = 'test_name'
testlist = ['test_name', 'test_name_dup', 'test_name_dup_1', 'test_name_dup_3']

我希望使用lambda来创建一个循环的函数,并返回第一个不在testlist中的test_name_#。该功能最终将应用于文件名,但出于测试目的,我不得不放弃实际读取文件名 - 给了我太多方法来搞砸了。

但my_test必须能够更改,测试列表将是文件路径列表。

所以,我正在寻找一个像:

这样的功能
new_name = lambda x: my_test + '_' + str(x)

但是初始值应该是x = 1,并且它应该继续,直到new_name不在testlist中。似乎是:

bool(new_name not in testlist)

可能适用。

但我无法找到一种方法将初始x设置为1,并使用(x + 1)循环直到bool为真。

我知道这是可能的,因为我发现了一些在文件中循环的CRAZY lambda示例。我只是无法理解它们(并且没有任何方式可以与它们一起玩,因为它们处理的是编程级别之外的事情。

在相关的说明中,我可以在此循环的开头添加值吗? (即我可以检查test_name,然后检查test_name_dup,然后检查test_name_dup _#)?

提前感谢您的帮助! Lambdas(虽然非常酷)完全弄乱了我的脑袋。

5 个答案:

答案 0 :(得分:6)

Lambdas只是定义函数的另一种方式

def foo(x):
    return x + x

相同
foo = lambda x: x + x

所以让我们从一个功能开始,做你想做的事情

def first_missing(items, base):
    for number in itertools.count():
        text = base + '_' + str(number)
        if text not in items:
             return text

首先要注意的是,你不能在lambda中使用循环。所以我们需要在没有循环的情况下重写它。相反,我们将使用递归

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        if text not in items:
             return text
        else:
             return first_missing(items, base, number + 1)

现在,我们也不能在lambda中使用if / else块。但我们可以使用三元表达式

def first_missing(items, base, number = 0):
        text = base + '_' + str(number)
        return text if text not in items else first_missing(items, base, number + 1)

我们不能在lambda中有局部变量,所以我们将使用一个技巧,默认参数

def first_missing(items, base, number = 0):
        def inner(text = base + '_' + str(number)):
            return text if text not in items else first_missing(items, base, number + 1)
        return inner()

此时我们可以将内部重写为lambda

def first_missing(items, base, number = 0):
        inner = lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1)
        return inner()

我们可以组合两行来摆脱内部局部变量

def first_missing(items, base, number = 0):
    return (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

最后,我们可以把整个事情变成一个lambda

first_missing = lambda: items, base, number = 0: (lambda text = base + '_' + str(number): text if text not in items else first_missing(items, base, number + 1))()

希望能让您深入了解自己能做些什么。但不是每个人都这样做。你可以告诉lambdas真的很难读。

答案 1 :(得分:2)

在这种情况下无需使用lambda,简单的for循环即可:

my_test  = 'test_name_dup'  
testlist = ['test_name', 'test_name_dup','test_name_dup_1', 'test_name_dup_3']

for i in xrange(1, len(testlist)):
    if my_test + '_' + str(i) not in testlist:
        break

print my_test + '_' + str(i)
> test_name_dup_2

如果你真的,真的想要使用lambda来解决这个问题,你还需要学习迭代工具,迭代器,过滤器等等。我将以thg435的答案为基础,将其写成一个更惯用的时尚并解释它:

import itertools as it

iterator = it.dropwhile(
    lambda n: '{0}_{1}'.format(my_test, n) in testlist,
    it.count(1))

print my_test + '_' + str(iterator.next())
> test_name_dup_2

理解上述解决方案的关键在于dropwhile()程序。它需要两个参数:谓词和可迭代,并返回一个迭代器,只要谓词为真,它就会从迭代中删除元素;之后,返回每个元素。

对于iterable,我传递count(1),这是一个从1开始生成无限整数的迭代器。

然后dropwhile()开始使用整数,直到谓词为假;这是传递内联定义函数的好机会 - 这是我们的lambda。它依次接收每个生成的整数,检查列表中是否存在字符串test_name_dup_#。

当谓词返回false时,dropwhile()会返回,我们可以通过调用next()来检索使其停止的值。

答案 2 :(得分:1)

您可以将lambda与itertools.dropwhile结合使用:

import itertools
n = itertools.dropwhile(lambda n: 'test_name_dup_%d' % n in testlist, range(1, len(testlist))).next()

关于你的上一个问题,你可以为名称编写一个生成器,例如:

def possible_names(prefix):
    yield prefix
    yield prefix + '_dup'
    n = 0
    while True:
        n += 1
        yield '%s_dup_%d' % (prefix, n)

然后使用dropwhile:

unique_name = itertools.dropwhile(lambda x: x in testlist, possible_names('test_name')).next()
print unique_name

答案 3 :(得分:1)

你有点偏离轨道。 Lambdas只不过是“简单”的函数,通常用于函数式编程中的快速语法。它们是完美的伴随内置函数“map”,“reduce”,“filter”,但也适用于itertools中定义的更复杂的函数。因此,最有用的是生成/操作可迭代对象(尤其是列表)。请注意,与列表推导/普通循环相比,lambdas会在大多数情况下减慢代码速度,并且会使其更难被阅读。这是一个你想用lambdas做什么的例子。

>>> filter(lambda i: i!=(0 if len(testlist[i].split("_"))==3 else int(testlist[i].split("_")[-1])), range(len(testlist)))[0]
2

或者你可以使用更复杂的函数和itertools。无论如何,我强烈建议你不要将lambdas用于这种任务,因为可读性很糟糕。我宁愿使用结构良好的for循环,这也更快。

<强> [编辑]

要证明lambdas + builtins并不比列表推导更快:考虑一个简单的问题,对于范围内的x(1000),创建一个x移位5的列表。

$ python -m timeit'map(lambda x:x&gt;&gt; 5,range(1000))'1000循环,最好的3: 225 usec 每个循环

$ python -m timeit'[x&gt;&gt; 5 for x in range(1000)]'10000循环,最好的3: 99.1 usec 每循环

没有lambdas,你的性能提升了100%。

答案 4 :(得分:1)

我更喜欢list comprehension或iterator方法。轻松制作一个我认为非常容易阅读和维护的衬垫。坦率地说,lambdas属于某些地方,在这里我相信它不太优雅的解决方案。

my_test = 'test_name'
prefix = 'test_name_dup_'
testlist = ['test_name','test_name_dup','test_name_dup_1','test_name_dup_3']

from itertools import count
print next('%s%d' % (prefix, i) for i in count(1) if '%s%d' % (prefix, i) not in testlist)

这将返回序列中第一个未找到的实例,我认为这是最干净的。

当然,如果您更喜欢明确范围内的列表,可以将其修改为列表理解:

print ['%s%d' % (prefix, i) for i in xrange(0,5) if '%s%d' % (prefix, i) not in testlist]

返回:

['test_name_dup_0', 'test_name_dup_2', 'test_name_dup_4']