5

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

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

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

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

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

我希望使用 lambda 创建一个循环并返回不在测试列表中的第一个 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(虽然很酷)完全弄乱了我的脑袋。

4

5 回答 5

6

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

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()

此时我们可以将 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))()

希望这能让您对自己能做什么有所了解。但是永远不要这样做,因为正如您所知,lambda 会使您的代码非常难以阅读。

于 2012-04-14T17:14:36.007 回答
2

在这种情况下不需要使用 a 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

如果您真的非常想使用 alambda来解决这个问题,您还必须了解 itertools、迭代器、过滤器等。我将以 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()过程。它有两个参数:一个谓词和一个迭代,并返回一个迭代器,只要谓词为真,它就会从迭代中删除元素;之后,返回每个元素。

对于可迭代对象,我传递count(1)了一个迭代器,它产生从 开始的无限数量的整数1

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

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

于 2012-04-14T16:42:59.870 回答
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
于 2012-04-14T16:51:50.383 回答
1

你有点偏离轨道。Lambda 只不过是“简单”函数,通常用于函数式编程中的快速语法。它们是完美的配套内置函数“map”、“reduce”、“filter”,也适用于定义在itertools中的更复杂的函数。因此,对它们最有用的事情是生成/操作可迭代对象(尤其是列表)。请注意,与列表推导/正常循环相比,在大多数情况下,lambda 会减慢您的代码速度,并且会使其更难阅读。这是您想要使用 lambda 执行的操作的示例。

>>> 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 in range(1000) 创建一个 x 移动 5 的列表。

$ python -m timeit 'map(lambda x: x>>5, range(1000))' 1000 个循环,最好的 3 个:每个循环225 微秒

$ python -m timeit '[x>>5 for x in range(1000)]'10000 次循环,最好的 3 次:每个循环99.1 微秒

在没有 lambda 的情况下,您的性​​能提升 >100%。

于 2012-04-14T16:59:11.200 回答
1

我更喜欢列表推导或迭代器方法。使我觉得很容易阅读和维护的一个衬里变得容易。坦率地说,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']
于 2012-04-14T17:16:42.870 回答