Python แก้ปัญหาชั่วคราวสำหรับการมอบหมายในแลมบ์ดา


34

นี่เป็นคำถามเคล็ดลับสำหรับการเล่นกอล์ฟใน Python

ใน Python golfing เป็นเรื่องปกติที่การส่งจะเป็นฟังก์ชั่นที่กำหนดเป็นแลมบ์ดา ตัวอย่างเช่น,

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

คำนวณแฟคทอเรียลของ x

รูปแบบแลมบ์ดามีข้อดีสองประการ :

  • หม้อไอน้ำของf=lambda x:...หรือlambda x:...สั้นกว่าdef f(x):...return...หรือx=input()...print...
  • การเรียกซ้ำสามารถใช้เพื่อวนซ้ำด้วยค่าใช้จ่ายในไบต์น้อย

อย่างไรก็ตามแลมบ์ดามีข้อเสียเปรียบครั้งใหญ่ที่อนุญาตให้ใช้เพียงนิพจน์เดียวโดยไม่มีคำสั่ง โดยเฉพาะอย่างยิ่งนี่หมายถึงไม่มีการมอบหมายใดc=chr(x+65)ๆ นี่เป็นปัญหาเมื่อมีนิพจน์ยาวที่ต้องอ้างอิงค่าสองครั้ง (หรือมากกว่า)

การมอบหมายเช่นE=enumerateนั้นเป็นไปได้นอกฟังก์ชั่นหรือเป็นอาร์กิวเมนต์ที่เป็นทางเลือก แต่ถ้ามันไม่ได้ขึ้นอยู่กับอินพุตของฟังก์ชัน อาร์กิวเมนต์ที่เป็นทางเลือกเช่นf=lambda n,k=min(n,0):...ล้มเหลวเนื่องจากอินพุตnไม่ได้ถูกกำหนดเมื่อkถูกประเมิน ณ เวลานิยาม

ผลที่ได้คือบางครั้งคุณดูดซ้ำการแสดงออกยาวในแลมบ์ดาเพราะทางเลือกคือแลมบ์ดาที่ไม่ยืดเยื้อ

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

จุดแบ่งแม้เป็นเรื่องเกี่ยวกับ 11 ตัว ( รายละเอียด ) ที่ผ่านมาที่คุณเปลี่ยนไปหรือdef programเปรียบเทียบสิ่งนี้กับจุดแตกหักคู่ปกติ 5 สำหรับการแสดงออกซ้ำ:

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

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

ภาษาอื่นมีวิธีแก้ปัญหาเช่นอ็อกเทฟ มีกลอุบายที่รู้จักกันดีสำหรับ Python แต่มีความยาว clunky และ / หรือการใช้งานที่ จำกัด วิธีการสั้น ๆ ที่ใช้งานทั่วไปเพื่อจำลองการมอบหมายในแลมบ์ดาจะปฏิวัติการเล่นกอล์ฟ Python


นักกอล์ฟ Python มีวิธีใดบ้างในการเอาชนะหรือหลีกเลี่ยงข้อ จำกัด นี้? พวกเขาควรมีความคิดที่เป็นไปได้อะไรบ้างเมื่อพวกเขาเห็นการแสดงออกที่ยาวนานซ้ำสองครั้งในแลมบ์ดา?

เป้าหมายของฉันสำหรับคำถามเคล็ดลับนี้คือดำดิ่งสู่ปัญหานี้และ:

  • แคตตาล็อกและวิเคราะห์วิธีแก้ปัญหาการเล่นกอล์ฟเพื่อการมอบหมายปลอมภายในแลมบ์ดา
  • สำรวจโอกาสในการขายใหม่สำหรับวิธีการที่ดีกว่า

คำตอบแต่ละข้อควรอธิบายวิธีแก้ปัญหาหรือโอกาสในการขายที่อาจเกิดขึ้น


ฉันเดาว่านี่เป็นหนึ่งในสิ่งที่ไม่สามารถทำได้ดีใน Python JavaScript มีขาขึ้นบนอันนี้
mbomb007

เช่นเดียวกับคำตอบของ orlp คำแนะนำของ Neil (ถูกลบ) สำหรับการใช้ lambdas ที่ซ้อนกันไม่จำเป็นต้องนานกว่า def ในกรณีที่คุณต้องการแลมบ์ดาที่ซ้อนกันอยู่แล้ว ฉันคิดว่ามันสมควรได้รับการวิเคราะห์อย่างละเอียดมากขึ้น
Martin Ender

2
สำหรับตัวอย่างที่แน่นอนให้กับ concatenation lambda s:(s+s[::-1]).lower()สตริงตัวพิมพ์เล็กตรงกันข้ามหนึ่งก็สามารถไป ของหลักสูตรนี้ไม่ตอบคำถามจริง
Jonathan Allan

จุด @JonathanAllan stripดีเปลี่ยนไป
xnor

คำตอบ:


6

eval

มันไม่ได้ยอดเยี่ยมขนาดนั้น แต่ถ้าโซลูชันของคุณใช้แล้วevalในบางวิธีหรือรูปแบบคุณสามารถใช้เทคนิคนี้ได้

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

ว้าวนั่นมันฉลาด! ทำไมฉันไม่เคยเห็นรหัสประเภทนี้
z0rberg ใน

6
@ z0rberg อาจเป็นเพราะความชั่วร้าย
HyperNeutrino

ฉันไม่สมัครสมาชิกเพื่อรับรหัสนี้ #EvalLivesMatter ... แต่อย่างจริงจังวิธีที่แย่กว่าการฉีด dll แบบง่ายๆ
z0rberg ใน

6

นิพจน์การกำหนดใน Python 3.8

Python 3.8 ( TIO ) แนะนำการแสดงออกที่ได้รับมอบหมายซึ่งใช้:=ในการกำหนดตัวแปรอินไลน์เป็นส่วนหนึ่งของการแสดงออก

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

สามารถใช้ภายใน a 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เป็นจำนวนของตัวแปร นี่เป็นค่าใช้จ่ายเริ่มต้นที่สูง แต่สามารถชำระได้ในที่สุด มันยังส่งคืนค่าภายในเพื่อให้คุณสามารถซ้อนหลาย ๆ อันได้ (แม้ว่ามันจะไม่คุ้มค่าอย่างรวดเร็ว)

สิ่งนี้มีประโยชน์จริง ๆ สำหรับการคำนวณระดับกลางที่ยาวนานในรายการที่ซ้อนกันซึ่ง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)

upsides

  • อนุญาตการมอบหมายใหม่ด้วยต้นทุนที่ลดลงเล็กน้อย (มีการประกาศรายการแล้ว)
  • เป็น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)

Upsides / Downsides

  • โดยพื้นฐานเหมือนกับรายการ
  • ใช้งานได้กับการใช้งานที่มีความยาวมากกว่าเท่านั้น 4

ข้อควรพิจารณาอื่น ๆ

  • หากเทคนิคนี้คุ้มค่ากับการลดตัวละครก็lambdaอาจจะลดลงและฟังก์ชั่นจะถูกเขียนใหม่ด้วยdef/ inputสำหรับโปรแกรมที่สั้นกว่า
  • ดังที่@FlipTackชี้ให้เห็นรายการและ dict ARE KEEPTระหว่างการเรียกใช้ฟังก์ชันในขณะที่สิ่งนี้ส่วนใหญ่จะรบกวนมันสามารถใช้กับการโทรซ้ำ สถานการณ์สูงอีกครั้ง
  • พจนานุกรมจะสั้นกว่ารายการเสมอ

3
การส่งฟังก์ชั่นจะต้องใช้งานได้มากกว่าหนึ่งครั้ง ปัจจุบันนี้ใช้รายการเดียวกันทุกครั้งที่แลมบ์ดาทำงานซึ่งอาจทำให้สับสนในการทำงานในภายหลัง
FlipTack

@FlipTack คุณสนใจที่จะเชื่อมโยงแหล่งที่มาเช่นเมตาหรือไม่ ฉันคิดว่ากฎอาจมีผลกระทบต่อเทคนิคเล็กน้อยที่ฉันรู้
JAD


ฉันคิดว่าคุณสามารถทำได้ดีกว่าด้วยพจนานุกรม: lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]มันสามารถใช้ซ้ำได้
Zgarb

คุณสามารถใช้list.extendเพื่อเพิ่มองค์ประกอบหลายรายการพร้อมกันซึ่งจะสั้นกว่าการใช้list.appendหลายครั้ง
mbomb007

0

ใช้เพื่อตั้งค่าตัวแปรและส่งคืนข้อมูลหลังจากการดำเนินการเช่น:

add = lambda x, y: exec("x+=y")

อีกทางหนึ่ง: {เพิ่ม = แลมบ์ดา x, y: [x.append (x [0] + y), x.reverse (), x.pop ()]}
Dylan Eliot

0

รายการความเข้าใจ

นี่เป็นอีกทางเลือกสุดท้ายเนื่องจากมันไม่สงบมากนัก แต่คุณสามารถ
[<expression> for <variable> in <value>]
ตั้งค่าตัวแปรในแลมบ์ดาได้ โดยพื้นฐานแล้วจุดที่ดีเพียงอย่างเดียวเกี่ยวกับวิธีนี้คือการแสดงออกภายในสามารถอ่านได้ซึ่งเป็นสิ่งที่คุณกังวลน้อยที่สุดเมื่อเล่นกอล์ฟ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.