ラムダでの割り当てのためのPythonの回避策


34

これはPythonでのゴルフのヒントの質問です。

Pythonゴルフでは、提出がラムダとして定義された関数であることが一般的です。例えば、

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

x の階乗計算します。

ラムダ形式には2つの大きな利点があります。

  • f=lambda x:...またはのボイラープレートはlambda x:...def f(x):...return...またはx=input()...print...
  • 再帰呼び出しを使用すると、ほとんどバイトのオーバーヘッドなしでループできます。

ただし、ラムダには、単一の式のみを許可し、ステートメントを許可しないという大きな欠点があります。特に、これはのような割り当てがないことを意味しc=chr(x+65)ます。値を2回(またはそれ以上)参照する必要がある長い式がある場合、これは問題になります。

のような割り当てE=enumerateは、関数の外部またはオプションの引数として可能ですが、関数の入力に依存しない場合のみです。定義時に評価されるときにf=lambda n,k=min(n,0):...入力nが定義されていないため、fail などのオプションの引数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

他の言語には、Octaveなどの回避策があります。Pythonには既知のトリックがありますが、それらは長く、不格好で、使用が制限されています。ラムダでの割り当てをシミュレートするための短く汎用的な方法は、Pythonゴルフに革命をもたらします。


Pythonゴルファーがこの制限を克服または回避する方法は何ですか?ラムダで長い表現が2回繰り返されるのを見るとき、彼らはどんな潜在的なアイデアを念頭に置くべきですか

このヒントの質問の私の目標は、この問題を深く掘り下げることです。

  • ゴルフの回避策をカタログ化して分析し、ラムダ内の偽の割り当て
  • より良い方法のための新しいリードを探る

各回答では、回避策または潜在的なリードを説明する必要があります。


これはPythonでうまくできないことの1つだと思います。JavaScriptはこれに足を踏み入れています。
mbomb007

orlpの答えと同様に、ネストされたラムダを使用するためのNeilの(削除された)提案は、とにかくネストされたラムダが必要な場合、必ずしもdefより長くなるわけではありません。もっと徹底的な分析に値すると思います。
マーティンエンダー

2
逆の小文字の文字列連結で与えられた正確な例については、ただ進むことができlambda s:(s+s[::-1]).lower()ます。もちろん、これは実際の質問には答えません。
ジョナサンアラン

@JonathanAllan良い点、それをに変更しましたstrip
-xnor

回答:


6

eval

これ自体はそれほど素晴らしいことではありませんが、ソリューションが既にeval何らかの方法または形式で使用されている場合、通常はこの手法を使用できます。

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

うわー、それはスマートです!この種のコードが表示されないのはなぜですか?
z0rbergの

6
@ z0rberg's evalは悪だからでしょう。
ハイパーニュートリノ

私はこのコーディズムを購読していません。#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です。ここでは変数の数です。これは初期費用が高くなりますが、結局は完済できます。内側の値も返すので、複数をネストできます(ただし、これはすぐに価値がなくなります)。

これは、ネストされたリスト内包表記の長い中間計算でのみ有効です。これはdef f():a=...;b=...;return通常a が短いためです。

値が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は指摘し、リストや辞書はKEEPT AREこれは主にディスターブ、それは再帰呼び出しで使用することができますが、関数呼び出しの間。再び非常に状況的な
  • 辞書は常にリストよりも短くなります。

3
関数の送信は複数回使用可能でなければなりません。現在、これはラムダが実行されるたびに同じリストを使用します。これは、後の実行で物事を台無しにする可能性があります。
-FlipTack

興味のない@FlipTack、メタのようなソースをリンクしてくれませんか?このルールは、私が知っているいくつかのトリックに何らかの影響を与える可能性があると思います。
JAD


辞書を使えばもっとうまくできると思います:lambda s,d={}:d.setdefault(0,s.lower())+d[0][::-1]再利用も可能です。
ズガルブ

list.extend一度に複数の要素を追加するために使用できます。これは、list.append複数回使用するよりも短くなります。
mbomb007


0

リスト内包表記

これはあまり手に負えないので、これは最後の手段
[<expression> for <variable> in <value>]
ですが、ラムダに変数を擬似的に設定することもできます。基本的に、この方法の唯一の良い点は、内側の表現を読みやすくすることができるということです。これは明らかに、ゴルフをするときのあなたの心配の最小です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.