ソフトウェアエンジニアなら1時間で解けるべき問題5に挑戦をごにょごにょしてたら、
コメントでpodhmoさんよりワンライナーいただきました。ありがとうございました。
(現物は元記事を御覧ください。)
眺めてもさっぱり意味が分からないので、バラして自分で分かる形にしてみようとやってみました。
のっけからまったく意味不明の流れ
(lambda f: (lambda x: x(x))(lambda y: f(lambda v: y(y)(v))))
ぐるぐるしてる感じは伝わってくるのですが...謎です。
そのままググってみたらYコンビネータというものだそうな。
無名関数のまま再帰できるんだそうです。
ごめんなさい。今はスルーさせていただきます。目が回ってきたので。
自分に分かるように普通に名前を付けてやってみた
こんな感じでしょうか?
- 核心部分はコピペ。
- Yコンビネータを取るにはどうしたら?を考える。
- mapをリスト内包表記に変える練習。
c=(lambda xs:
[ [xs[0]] ] if len(xs) <= 1 else
__import__("itertools").chain(
[ [xs[0], ""] + y for y in c(xs[1:]) ],
[ [xs[0], " + "] + y for y in c(xs[1:]) ],
[ [xs[0], " - "] + y for y in c(xs[1:]) ]
)
)
print( "\n".join(
[ x for x in
[ "".join(str(y) for y in z )
for z in c(range(1, 10 ) )
]
if eval(x) == 100
]
)
)
- すべての文字の組み合わせのリストのイテレータを返す関数cに
- range(1,10)を適用して
- 結合した文字列のうち
- 評価したら100になるものを
- printする
ということでしょうか。
自分も最初そうするのかなと思ったんですが、じゃあどうしたらいいかが分かりませんでした。勉強になりました。
ところで...
def内には再帰関数を置けるが、lambda内には置けない
ようです。 できました。後述。
defでq_5_1の中にcを定義した下のはちゃんと動きました。
def q_5_1():
def c(xs):
return ([[xs[0]]] if len(xs) <= 1 else
__import__("itertools").chain(
[[xs[0], ""] + y for y in c(xs[1:])],
[[xs[0], " + "] + y for y in c(xs[1:])],
[[xs[0], " - "] + y for y in c(xs[1:])]
)
)
print("\n".join(
[ x for x in
[ "".join(str(y) for y in z )
for z in c(range(1, 10 ) )
]
if eval(x) == 100
]
)
)
q_5_1()
lambdaで同じことをしようとするとうまくいきませんでした。
def pr(x):
print x
q_5_2=(lambda:
(lambda c=(lambda xs:
[[xs[0]]] if len(xs) <= 1 else
__import__("itertools").chain(
[[xs[0], ""] + y for y in c(xs[1:])],
[[xs[0], " + "] + y for y in c(xs[1:])],
[[xs[0], " - "] + y for y in c(xs[1:])]
)
):
pr( "\n".join(
[ x for x in
[ "".join(str(y) for y in z )
for z in c(range(1, 10 ) )
]
if eval(x) == 100
]
)
)
)()
)
q_5_2()
"NameError: global name 'c' is not defined"とか怒られました。
lambdaでは引数は外側の環境を参照するから自分自身は呼べないっていうことでしょうか...
Yコンビネータとかがあるのもこの辺と関係があるんでしょうか。
とはいえ、複雑なものは簡単な関数に分けて考えろというのがすごいH神の教え?なので、当面は特に困らないかもです。
※できました。
# coding: utf-8
do = box = lambda *x : x
switch = unbox = lambda x : x[-1]
otherwise = True
q_5_2=( lambda :
( lambda c = ( lambda xs :
( lambda f : f( f, xs ) )(lambda f, xs :
switch(
len(xs) <= 1 and do( [[xs[0]]] )
or otherwise and do( __import__("itertools").chain(
[[xs[0], ""] + y for y in f( f, xs[1:])],
[[xs[0], " + "] + y for y in f( f, xs[1:])],
[[xs[0], " - "] + y for y in f( f, xs[1:])]
)
)
)
)
):
print( "\n".join(
[ x for x in
[ "".join(str(y) for y in z )
for z in c(range(1, 10 ) )
]
if eval(x) == 100
]
)
)
)()
)
q_5_2()
さらに簡潔に:
# coding: utf-8
do = box = lambda *x : x
switch = unbox = lambda x : x[-1]
otherwise = True
q_5_2 = lambda : (
lambda c = lambda xs , f = lambda f, xs :
switch(
len(xs) <= 1 and do( [[xs[0]]] )
or otherwise and do( __import__("itertools").chain(
[[xs[0], ""] + y for y in f( f, xs[1:])],
[[xs[0], " + "] + y for y in f( f, xs[1:])],
[[xs[0], " - "] + y for y in f( f, xs[1:])]
)
)
)
: f( f, xs )
:
print( "\n".join(
[ x for x in
[ "".join(str(y) for y in z )
for z in c(range(1, 10 ) )
]
if eval(x) == 100
]
)
)
)()
q_5_2()