在Lambda中分配的Python解决方法


34

这是使用Python打高尔夫球的提示问题。

在Python打高尔夫球中,提交通常是定义为lambda的函数。例如,

f=lambda x:0**x or x*f(x-1)

计算 x 的阶乘

lambda格式有两个主要优点

  • f=lambda x:...或的样板lambda x:...def f(x):...return...或短x=input()...print...
  • 可以使用递归调用以很少的字节开销进行循环。

但是,lambda具有一个很大的缺点,即只允许一个表达式,没有语句。特别是,这意味着没有类似的分配c=chr(x+65)。当一个人有一个长表达式需要将其值引用两次(或更多)时,这是有问题的。

类似E=enumerate的赋值可以在函数外部或作为可选参数,但前提是它们不依赖于函数输入。可选参数f=lambda n,k=min(n,0):...失败,因为n在定义时k评估输入时尚未定义输入。

结果是有时您会因为在lambda中重复一个长表达式而感到厌烦,因为替代方案是一个较长的非lambda。

lambda s:s.strip()+s.strip()[::-1]
def f(s):t=s.strip();print t+t[::-1]

收支平衡点约为11个字符(详细信息),超过此字符您将切换到a defprogram。将此与通常的长度为5的收支平衡点进行比较以得到重复的表达式:

range(a)+range(b)
r=range;r(a)+r(b)

print s[1:],s[1:]*2
r=s[1:];print r,r*2

其他语言也有解决方法,例如Octave。有一些已知的Python技巧,但它们使用时间长,笨拙和/或用途有限。一种用于在lambda中模拟分配的简短通用方法将彻底改变Python高尔夫。


Python高尔夫球手有哪些方法可以克服或解决此限制?当他们看到一个长表达式在lambda中重复两次时,他们应该想到什么潜在的想法?

我的这个提示问题的目标是深入研究此问题,并:

  • 编目并分析高尔夫变通方法以在lambda中进行假分配
  • 探索新的线索以寻求更好的方法

每个答案都应说明解决方法或潜在的领先优势。


我猜这是Python无法很好完成的事情之一。JavaScript支持这一点。
mbomb007'1

就像orlp的答案一样,在您仍然需要嵌套lambda的情况下,Neil(已删除)使用嵌套lambda的建议不一定比def长。我认为值得进行更彻底的分析。
Martin Ender

2
对于反向小写字符串串联给出的确切例子,可以用lambda s:(s+s[::-1]).lower()。当然,这并不能回答实际问题。
乔纳森·艾伦

@JonathanAllan好点,将其更改为strip
xnor

Answers:


6

eval

这本身并不是很好,但是如果您的解决方案已经eval以某种方式或形式使用,则通常可以使用此技术。

eval("%f*%f+%f"%((5**.5,)*3))

哇,真聪明!为什么我永远都看不到这种代码?
z0rberg

6
@ z0rberg的原因可能是eval是邪恶的。
HyperNeutrino

我不同意这种编码。#EvalLivesMatter ...但是说真的,这比简单的dll注入还要糟糕吗?
z0rberg

6

Python 3.8中的赋值表达式

Python 3.8TIO)引入了赋值表达式:=用于将内联变量作为表达式的一部分进行赋值。

>>> (n:=2, n+1)
(2, 3)

可以在lambda通常不允许分配的内使用此功能。比较:

lambda s:(t:=s.strip())+t[::-1]
lambda s:s.strip()+s.strip()[::-1]
def f(s):t=s.strip();return t+t[::-1]

有关更多信息,请参见此技巧


2

内层λ

这些使您可以一次定义多个变量。

lambda s:s.strip()+s.strip()[::-1]

lambda s:(lambda t:t+t[::-1])(s.strip())

更长,但是如果您有多个变量或更长的变量,这些变量会重复多次:

lambda a,b,c:a.upper()*int(c)+b.lower()*int(c)+a.upper()[::-1]+b.lower()[::-1]+a.upper()*int(c)+a.lower()*int(c)

lambda a,B,c:(lambda A,b,n:A*n+b*n+A[::-1]+b[::-1]+A*n+b*c)(a.upper(),B.lower(),int(c))

字符数

初始值:(lambda:)()(11个字节)
第一个变量:[space]a(2个字节)
后续变量:,b,(3个字节)
使用:a(1个字节)。

(lambda* a*_,b_:)(*<value a>*_,<value b>_)

(也保存在括号中)

因此,这需要3n + 10字节,其中n是变量数。这是很高的初始成本,但最终可以还清。它甚至返回其内部值,因此您可以嵌套多个(尽管这很快变得不值得。)

实际上,这仅对嵌套列表推导中的长中间计算有用,因为a def f():a=...;b=...;return通常会更短。

对于1值,这将保存:uses * length - length - uses - 13,因此仅在该表达式为正数时才有用。
对于总计n使用u时间不同的表达式,其总长度为l,这样可以节省:
l - (3 * n) - u - 10 ( + brackets removed )


1

使用清单

声明列表作为参数,并使用.append() or存储的值:
lambda s:s.lower()+s.lower()[::-1]
转成
lambda s,l=[]:l.append(s.lower())or l[-1]+l[-1][::-1]

字符数:

,l=[]5个字符
l.append()or13
l[-1]个字符每次使用5个字符

收支平衡

添加的字符数为:
uses*(5-length) + 18 + length
在前面的示例中,该语句s.lower()长9个字符,并且使用了2次,应用此技术后添加了19个字符。如果使用7次,将减少1个字符。
对该技术的最小使用量值得
min_uses = (18+length)/(length-5)

优点

  • 允许以略微降低的成本进行新分配(该列表已经声明)
  • 是一个list对象,因此[0].pop()[x:y]等列表功能可用于技巧。高度情境

缺点

  • 初期成本高
  • 使用成本高
  • 仅适用于长度大于 5

使用字典

感谢@Zgarb
与上面相同的想法声明一个字典作为参数并用于.setdefault()存储(并返回)值:
lambda s:s.lower()+s.lower()[::-1]
变成
lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]
注意,与list对应的字典不同,它setdefault返回分配的值。

字符数:

,d={}5个字符
d.setdefault(k,)16
d[k]个字符每次使用4个字符

收支平衡

添加的字符数为:
(uses-1)*(4-length) + 21
在前面的示例中,该语句s.lower()长9个字符,并且使用了2次,应用此技术后添加了16个字符。如果使用7次,将减少1个字符。
对该技术的最小使用量值得
min_uses = 1-21/(4-length)

优点/缺点

  • 基本上与清单相同
  • 仅适用于长度大于 4

其他注意事项

  • 如果该技术值得减少字符,则lambda可以删除,并使用def/ 重写功能以input缩短程序长度。
  • 就像@FlipTack指出的那样,列表和字典在函数调用之间是相同的,尽管这通常会打扰到他人,但它可用于递归调用。再次高度情境
  • 字典将永远比列表短。

3
功能提交必须多次使用。当前,每次运行lambda时,它使用相同的列表,这可能会在以后的运行中弄乱事情。
FlipTack

@FlipTack出于兴趣,您介意链接类似meta的源吗?我认为该规则可能会影响我所知道的一些技巧。
JAD


我认为您可以用字典做得更好:lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]它也是可重用的。
Zgarb

您可以使用list.extend一次添加多个元素,这比使用list.append多次要短。
mbomb007'1


0

清单理解

这是最后一招,因为它太笨拙了,但是您可以
[<expression> for <variable> in <value>]
在lambda中伪设置变量。基本上,此方法的唯一优点是内部表达式可以保持可读性,这显然是打高尔夫球时您最不用担心的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.