1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python:lambdaで問題5に挑戦...しないでワンライナーに深入り...しない

Last updated at Posted at 2015-05-31

ソフトウェアエンジニアなら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()

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?