ひるあんどんブログ

色々なことに手を出してみるブログ

The end of other

語訓練のために、私たちのロボットはサフィックス(接尾辞)について学ぼうとしています。

このタスクであなたは小文字の単語の集合(set)が与えられます。 ある単語が別の単語の末尾である(別の単語の接尾辞である)ような単語の対があるかどうか調べてください。 例えば: {"hi", "hello", "lo"} -- "lo"は"hello"の末尾です。したがって結果は真(True)です。

ヒント: このタスクのために集合(set)型で繰り返す方法と文字列データ型の関数に知っていなければいけません。 より詳しくはここの集合(set)型と ここの文字列関数を読んでください。

入力: 複数の単語、文字列の集合。

出力: TrueまたはFalse、ブール型。

例:

checkio({"hello", "lo", "he"}) == True

checkio({"hello", "la", "hellow", "cow"}) == False

checkio({"walk", "duckwalk"}) == True

checkio({"one"}) == False

checkio({"helicopter", "li", "he"}) == False


どのように使われるか: ここであなたは集合型で繰り返す方法と文字列型の関数について学ぶことができます。

事前条件: 2 ≤ len(words) < 30
all(re.match(r"\A[a-z]{1,99}\Z", w) for w in words)

def checkio(words_set):
    
    for b in words_set:
        for a in words_set:
            if a != b:
                if a.endswith(b): return True
                
    return False
    
#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    assert checkio({"hello", "lo", "he"}) == True, "helLO"
    assert checkio({"hello", "la", "hellow", "cow"}) == False, "hellow la cow"
    assert checkio({"walk", "duckwalk"}) == True, "duck to walk"
    assert checkio({"one"}) == False, "Only One"
    assert checkio({"helicopter", "li", "he"}) == False, "Only end"


接尾語にかんしてはendswithで調べられる。

str.endswith(suffix[, start[, end]])

    文字列が指定された suffix で終わるなら True を、そうでなければ False を返します。 suffix は見つけたい複数の接尾語のタプルでも構いません。オプションの start があれば、その位置から判定を始めます。オプションの end があれば、その位置で比較を止めます。

集合型の中のすべての文字の組み合わせについて調べる必要があるので、ループ2つでまわした。もちろん、自分を自分自身と比較しないようにした。


ただ、ループ2つって読みにくいのであんまり良い解答ではなさそう。

Clear


def checkio(words):

    """

    You can iterate throught set.

    """

    for w1 in words:

        for w2 in words:

            if w1 != w2 and (w1.endswith(w2) or w2.endswith(w1)):

                return True

    return False

一位の人もループ2つ使っているなぁ。ただ、組み合わせを同時に2つ調べて効率を良くしている。

Count Inversions

コンピューター科学と離散数学では、 転倒は 自然な順番に並んでいない要素の配列の場所のペアである。 番号のグループを昇順で使うとしたら転倒とは大きい番号が小さい番号の前に現れる時である。

この配列の例をチェックしてみましょう。 (1, 2, 5, 3, 4, 7, 6) には3つの転倒があります。

  • 5 と 3
  • 5 と 4
  • 7 と 6

あなたはユニークな番号の配列が与えられ、その配列の転倒数を数えなくてはいけません。

入力 配列が整数のタプルとして与えられる

出力 転倒数を整数として出力する

count_inversion*1 == 3

count_inversion*2 == 0



どうやって使われるか このミッションではネストされたループの不思議を経験することになるでしょう、 もちろんもしあなたが高度なアルゴリズムを使わなければですが。

事前条件 2 < len(sequence) < 200
len(sequence) == len(set(sequence))
all(-100 < x < 100 for x in sequence)

def count_inversion(sequence):
    """
        Count inversions in a sequence of numbers
    """
    current_number = sequence[0]
    first_count_loop = 0
    second_count_loop = 0
    result = 0
    length = len(sequence)
    
    while first_count_loop < length:
        second_count_loop = first_count_loop + 1
        current_number = sequence[first_count_loop]
        while second_count_loop < length:
            
            if current_number > sequence[second_count_loop]:
                result += 1
            
            second_count_loop += 1
        
        first_count_loop += 1
        
    
    return result


if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert count_inversion((1, 2, 5, 3, 4, 7, 6)) == 3, "Example"
    assert count_inversion((0, 1, 2, 3)) == 0, "Sorted"
    assert count_inversion((99, -99)) == 1, "Two numbers"
    assert count_inversion((5, 3, 2, 1, 0)) == 10, "Reversed"

すぐにはわからなかった。test =(1,2,5,3,4,7,6)というタプルで動きを考えてみる。

まず添字0から基準をはじめる。test[0]の値は1なので、これと右の数字を比較していく。本来の順番であれば、すべて右にいくほど(添字があがっていくほど)大きい値になるはず。
だから、test[0] = 1 より小さいのがあればカウンターをまわしていく。

右端まで行ったら、おわり。つぎは添字を一つ増やしてtest[1]について同様に比較していく。


という考え方で解いた。つまり、問題文にもあったようにループは2つ必要となる。

現在の数字を保存する変数と、外側のループ、内側のループを管理する変数が必要。
first_count_loopじゃなくて、first_loop_countのほうが良い名前だった…。

あとは素直に書いた。一応動く。


Clear


def count_inversion(sequence):

    return sum(sum(m<n for m in sequence[i+1:]) for i,n in enumerate(sequence))

ワンライナーだ。知らなかった関数は、以下の通り

enumerate(iterable, start=0)¶(原文)

    enumerate オブジェクトを返します。 iterable は、シーケンスか iterator か、あるいはその他のイテレーションをサポートする何らかのオブジェクトでなければなりません。 enumerate() によって返されたイテレータの __next__() メソッドは、 (デフォルトでは 0 となる start からの) カウントと、 iterable 上のイテレーションによって得られた値を含むタプルを返します。

    >>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    >>> list(enumerate(seasons))
    [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
    >>> list(enumerate(seasons, start=1))
    [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

    次と等価です:

    def enumerate(sequence, start=0):
        n = start
        for elem in sequence:
            yield n, elem
            n += 1

sum(iterable[, start])

    start と iterable の要素を左から右へ合計し、総和を返します。 start はデフォルトで 0 です。 iterable の要素は通常は数値で、start の値は文字列であってはなりません。

    使う場面によっては、 sum() よりもいい選択肢があります。文字列からなるシーケンスを結合する高速かつ望ましい方法は ''.join(sequence) を呼ぶことです。浮動小数点数値を拡張された精度で加算するには、 math.fsum() を参照下さい。一連のイテラブルを連結するには、 itertools.chain() の使用を考えてください。

Speedly

def M(l,r):

    if l+1>=r: return 0

    if l+2==r:

        if A[l]<=A[l+1]: return 0

        A[l],A[l+1]=A[l+1],A[l]

        return 1

    m = (l+r)//2

    i=l

    j=m

    k=l

    cl=M(l,m)

    cr=M(m,r)

    c=0

    while i<m and j<r:

        if A[i]<=A[j]:

            W[k]=A[i]

            k+=1

            i+=1

        else:

            W[k]=A[j]

            k+=1

            j+=1

            c+=m-i

    while i<m:

        W[k]=A[i]

        k+=1

        i+=1

    while j<r:

        W[k]=A[j]

        k+=1

        j+=1

    for i in range(l,r): A[i]=W[i]

    return cl+cr+c

<200b>

def count_inversion(a):

    global A,W

    A=list(a)

    W=[0]*len(A)

    return M(0,len(A))

<||





これはマージソートというらしい。
計算量がO(nlogn)なので大量になると早いみたい。

*1:1, 2, 5, 3, 4, 7, 6

*2:0, 1, 2, 3

Right to Left


「何世紀もの間、左利きの人達は右利きの人達のために設計された世界でアンフェアな差別をこうむってきた」
Santrock, John W. (2008). Motor, Sensory, and Perceptual Development.

「ほとんどの人々(70から95%と言われる)は右利きで、少数派(5から35%と言われる)は左利きで、はっきりとしない数の人々はおそらく両利きというのが最もあっている。
Scientific American. www.scientificamerican.com

ロボットのうちの一人は単純なタスクを課せられています。 連続した文字列を一つの文につなげて船を迂回する手順書を作るタスクです。 しかしこのロボットは左利きで、冗談を飛ばし右利きの友人を困惑させる性質があります。

あなたは連続した文字列を与えられます。 あなたはそれらの文字列をカンマで区切られたテキストの塊につなげなければなりません。 右利きのロボットへジョークを飛ばすように、他の単語の一部分であってもすべての"right"の単語を"left"に変えなければいけません。 すべての文字列は小文字で与えられます。

入力 連続した文字列が文字列(unicode)のタプルとして与えられる

出力 テキストを文字列として出力する

left_join*1 == "left,left,left,stop"

left_join*2 == "bleft aleft,ok"

left_join*3 == "bleftness wleft"

left_join*4 == "enough,jokes"


どうやって使われるか これは文字列と配列を使った操作の単純な例です。

事前条件
0 < len(phrases) < 42

def left_join(phrases):
    """
        Join strings and replace "right" to "left"
    """
    text =','.join(phrases)
    
    text = text.replace("right", "left")
        
        
    return text
        
    
        
    

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert left_join(("left", "right", "left", "stop")) == "left,left,left,stop", "All to left"
    assert left_join(("bright aright", "ok")) == "bleft aleft,ok", "Bright Left"
    assert left_join(("brightness wright",)) == "bleftness wleft", "One phrase"
    assert left_join(("enough", "jokes")) == "enough,jokes", "Nothing to replace"

joinも、replaceも非破壊的なのか。pythonの破壊的なメソッドの一覧がみつからない。Rubyみたく!で書いてくれるとわかりやすいんだけどなぁ。


これは他にあんまり解答ないみたいで、他の人も似たような書き方だった。

*1:"left", "right", "left", "stop"

*2:"bright aright", "ok"

*3:"brightness wright",

*4:"enough", "jokes"

Boolean Algebra

数学と数学的論理では ブール代数 は代数の部分領域であり、変数の値が真(true)と偽(false)で一般的にそれぞれ1と0で示されます。 変数の値が数字で主な演算が加算と乗算である基本的な代数学の代わりに、ブール代数の主な演算は論理積(conjunction) (∧で示される)と、論理和(disjunction)(∨で示される)と否定(negation)(¬で示される)です。

このミッションではあなたはいくつかのブール演算を実装する必要があります。

  • 論理積 "conjunction" x ∧ y で示され、 x = y = 1 の時は x ∧ y = 1 でそれ以外は x ∧ y = 0 を満たします。
  • 論理和 "disjunction" x ∨ y で示され、x = y = 0 の時は x ∨ y = 0 でそれ以外は x ∨ y = 1 を満たします。
  • 含意 "implication" (具象的含意) x→y で示され ¬ x ∨ y でも表せられます。もしxが真ならば、x → yはyの値です。xが偽ならばyの値は無視されるかもしれません、しかしながらこの演算は多少の真実の値を返さなくてはいけなくて2つの選択肢しかありません、なので返却値は必然的に少なくなり、はっきり言うと真です。
  • 排他 "exclusive" (排他的 or) x ⊕ y で示され、(x ∨ y)∧ ¬ (x ∧ y)でも表せられます。 それはxとy両方の可能性を排除します。算数の用語での定義では足して2で割った余りで、ここでは1+1=0になります。
  • 等価 "equivalence" x ≡ y で示され、¬ (x ⊕ y)でも表せられます。xとyが同じ値を取る時だけ真になります。

これらの演算の真の表は以下のようになります。

x | y | x∧y | x∨y | x→y | x⊕y | x≡y |

0 | 0 | 0 | 0 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 | 1 | 0 |
0 | 1 | 0 | 1 | 1 | 1 | 0 |
1 | 1 | 1 | 1 | 1 | 0 | 1 |


あなたには x と y の2つのブール値が1と0の値として与えられ、先ほど述べた演算の名前が与えられます。あなたはその値を計算し、1か0の値を返さなくてはいけません。

入力 3つの引数。XとYが0か1で与えられ、演算名が文字列で与えられます。

出力 結果を1か0で出力します。

boolean(1, 0, "conjunction") == 0

boolean(0, 1, "exclusive") == 1

1

2

3

どうやって使われるか ここではブール値と演算がどうやって扱われるかを学ぶでしょう。ブール値としての数字についても考えることになるでしょう。

事前条件 x in (0, 1)
y in (0, 1)
operation in ("conjunction", "disjunction", "implication", "exclusive", "equivalence")

OPERATION_NAMES = ("conjunction", "disjunction", "implication", "exclusive", "equivalence")

def boolean(x, y, operation):
    if operation == "conjunction":
        if x == y == 1:
            return 1
        return 0
    elif operation == "disjunction":
        if x == y == 0:
            return 0
        return 1
    elif operation == "implication":
        if x == 1:
            return y
        return 1
    elif operation == "exclusive":
        return (x + y) % 2
    elif operation == "equivalence":
        if x == y:
            return 1
        return 0
        

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert boolean(1, 0, "conjunction") == 0, "and"
    assert boolean(1, 0, "disjunction") == 1, "or"
    assert boolean(1, 1, "implication") == 1, "material"
    assert boolean(0, 1, "exclusive") == 1, "xor"
    assert boolean(0, 1, "equivalence") == 0, "same?"

美しさの欠片もないけど、素直に解いた。一回目に exclusiveを return x + y % 2ってやって死んだ。演算子の優先順位を忘れていた。

(優先順位が高い)
  +  -  ~x      (+と-は単項演算子)
  **
  *  /  %  //
  +  -
  <<  >>
  &
  ^
  |
  <  <=  >  >=  ==  !=  <>  is  is not  in  not in
  not
  and
  or
(優先順位が低い)

なので、(x + y) % 2 としないと死ぬ。


Clear 1位

def boolean(x, y, operation):

    if operation == "conjunction": return x & y

    if operation == "disjunction": return x | y

    if operation == "implication": return (1 ^ x) | y

    if operation == "exclusive":   return x ^ y

    if operation == "equivalence": return x ^ y ^ 1

    return 0

これは何だ? ビット演算だ。

通常および長整数型ではさらに、ビット列に対してのみ意味のある演算をサポートしています。負の数はその値の 2 の補数の値として扱われます (長整数の場合、演算操作中にオーバフローが起こらないように十分なビット数があるものと仮定します) 。

2 進のビット単位演算は全て、数値演算よりも低く、比較演算子よりも高い優先度です; 単項演算 ~ は他の単項数値演算 (+ および -) と同じ優先度です。

以下のテーブルでは、ビット列演算を優先度の低いものから順に並べています。 :
演算 	結果 	注釈
x | y 	ビット単位の x と y の 論理和 	 
x ^ y 	ビット単位の x と y の 排他的論理和 	 
x & y 	ビット単位の x と y の 論理積 	 
x << n 	x の n ビット左シフト 	(1)(2)
x >> n 	x の n ビット右シフト 	(1)(3)
~x 	x のビット反転 	 

注釈:

    負値のシフト数は不正であり、 ValueError が送出されます。
    n ビットの左シフトは、 pow(2, n) による乗算と等価です。結果が通常の整数の範囲を越えるときには、長整数が返されます。
    n ビットの右シフトは、 pow(2, n) による除算と等価です。

speedly

OPERATION_NAMES = ("conjunction", "disjunction", "implication", "exclusive", "equivalence")


boolean=lambda x,y,o:{'co':x and y,'di':x or y,'im':y if x else 1,'ex':x!=y,'eq':x==y}[o[:2]]

creative

boolean=lambda x,y,o:1&~"dimpleqonx".index(o[1])>>y>>x>>y

なにがなんだか、わからないよ!!(この回答は数学者によるものなんだって〜)

The Most Numbers

数を使ってみましょう。

数(浮動小数点数)の配列が与えられます。 あなたは最大の要素と最少の要素の差を求めなければいけません。 あなたの関数は不定個の入力パラメータを扱えなければいけません。 空の入力パラメータについては、0を返さなければいけません。

浮動小数点数はコンピュータハードウェアの内部では基数2(2進数)の分数で表現されています。 (ここを読んでみてください)。 我々は±0.001の精度で結果を判定します。

ヒント: 以下を使って、どのように動作するか考えてください。 任意個の関数パラメータ.

入力: 任意個の関数パラメータ、数 (整数、浮動小数点数)。

出力: 最大値と最小値の差、数 (整数、浮動小数点数)。

例:

checkio(1, 2, 3) == 2

checkio(5, -5) == 10

checkio(10.2, -2.2, 0, 1.1, 0.5) == 12.4

checkio() == 0


どのように使われるか: ここであなたは不定個の関数パラメータが渡されることについて学ぶでしょう。

事前条件: 0 ≤ len(args) ≤ 20
all(-100 < x < 100 for x in args)
all(isinstance(x, (int, float) 100 for x in args)

def checkio(*args):
    if len(args) == 0: return 0
    max_n = max(args)
    min_n = min(args)
    return max_n - min_n 

#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    def almost_equal(checked, correct, significant_digits):
        precision = 0.1 ** significant_digits
        return correct - precision < checked < correct + precision

    assert almost_equal(checkio(1, 2, 3), 2, 3), "3-1=2"
    assert almost_equal(checkio(5, -5), 10, 3), "5-(-5)=10"
    assert almost_equal(checkio(10.2, -2.2, 0, 1.1, 0.5), 12.4, 3), "10.2-(-2.2)=12.4"
    assert almost_equal(checkio(), 0, 3), "Empty"

素直にこうやって書いた。


Clear 1位

def checkio(*args):

    return max(args) - min(args) if args else 0

あー、そっかぁ。if argsでargsがあればTrueになるから、なかったらFalseになるのかぁ。賢いなぁ。

creative

checkio=lambda*a:len(a)and max(a)-min(a)

len(a)が0(False)じゃなくてってことか。

Three words

ワードと数を識別することをロボットに教えましょう。

空白(1つのスペース)で区切られたワードと数をもつ文字列が与えられます。 ワードは文字だけを含んでいます。 あなたは文字列が連続した三つのワードを含んでいるかどうかをチェックしなければいけません。 例えば、文字列"start 5 one two three 7 end"は連続した三つのワードを含んでいます。

ヒント: 以下の役に立つ関数を使ってこのタスクを簡単に解くことができます: str.split, str.isalpha, str.isdigit。

入力: ワードをもつ文字列

出力: 答え、ブール型

例:

checkio("Hello World hello") == True

checkio("He is 123 man") == False

checkio("1 2 3 4") == False

checkio("bla bla bla bla") == True

checkio("Hi") == False



どのように使われるか: このミッションであなたは文字列の使い方を学び、いくつかの役に立つ関数について知ることになります。

事前条件: 入力はワードと数の両方、またはワードのみ、または数のみを含みます。 (文字と数字の両方を含む)混在したワードは存在しません。
0 < len(words) < 100

def checkio(words):
    count = 0
    for x in words.split():
        if x.isalpha(): count += 1
        if count >= 3: 
            return True
            break
        if x.isdigit(): count = 0
        
    return False
            
        

#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    assert checkio("Hello World hello") == True, "Hello"
    assert checkio("He is 123 man") == False, "123 man"
    assert checkio("1 2 3 4") == False, "Digits"
    assert checkio("bla bla bla bla") == True, "Bla Bla"
    assert checkio("Hi") == False, "Hi"

3つのワードを含んでいます。っていう日本語が謎だったけど、要は単語が連続で3つ並んでたらTrueを返してね。って問題だった。
ヒントに書いてあるやつでisdigit,isalphaは前にも使った。splitは知らないので調べた。

str.split(sep=None, maxsplit=-1)

    文字列を sep をデリミタ文字列として区切った単語のリストを返します。maxsplit が与えられていれば、最大で maxsplit 回分割されます (つまり、リストは最大 maxsplit+1 要素になります)。 maxsplit が与えられないか -1 なら、分割の回数に制限はありません (可能なだけ分割されます)。

    sep が与えられた場合、連続した区切り文字はまとめられず、空の文字列を区切っていると判断されます(例えば '1,,2'.split(',') は ['1', '', '2'] を返します)。引数 sep は複数の文字にもできます (例えば '1<>2<>3'.split('<>') は ['1', '2', '3'] を返します)。区切り文字を指定して空の文字列を分割すると、 [''] を返します。

    例えば:

    >>> '1,2,3'.split(',')
    ['1', '2', '3']
    >>> '1,2,3'.split(',', maxsplit=1)
    ['1', '2,3']
    >>> '1,2,,3,'.split(',')
    ['1', '2', '', '3', '']

    sep が指定されていないか None であれば、異なる分割アルゴリズムが適用されます。 : 連続する空白文字はひとつのデリミタとみなされます。また、文字列の先頭や末尾に空白があっても、結果の最初や最後に空文字列は含まれません。よって、空文字列や空白だけの文字列を None デリミタで分割すると [] が返されます。

    例えば:

    >>> '1 2 3'.split()
    ['1', '2', '3']
    >>> '1 2 3'.split(maxsplit=1)
    ['1', '2 3']
    >>> '   1   2   3   '.split()
    ['1', '2', '3']

ようするに文字列を空白で切ってリストにして返すのか。これで単語単位に分割できるので、あとは文字列か数字なのかを調べればいいのだなぁ。


Clearの一位


def checkio(words):

    succ = 0

    for word in words.split():

        succ = (succ + 1)*word.isalpha()

        if succ == 3: return True

    else: return False

なにやってんのか、解読できなかった。なんだこれ…?

Speedlyの一位

import re


def checkio(words):

    return True if re.search('\D+\s\D+\s\D+', words) else False


これは、正規表現のreをインポートしてから正規表現でマッチしているのか…。
\Dが数字なのはいいけど、\sと\Sの違いがわからんかったので、ググった。


\d 数字 [0-9]
\D 非数字 [^0-9]
\s 空白文字 [\n\t\r\f\v]
\S 非空白文字 [^\n\t\r\f\v]

大文字と小文字で意味が逆になるのか…。


Cleativeの一位は


checkio=lambda x:"www" in "".join('w' if w.isalpha() else 'd' for w in x.split())

変態だな。

Secret Message

「賢い人はどこに葉を隠す?その森の中でだ。 しかし森がなかったら彼はどうするのだ? …彼はそれを隠すために森を育てる」

    • ギルバート・ケイス・チェスタートン

郵便を使わずに誰かに秘密のメッセージを送ったことがありますか? あなたは秘密を教えるために新聞を使うことが出来る。 もし誰かがあなたのメッセージを見つけたとしても、その偏執さや偽の海賊理論をはねのけるのは容易い。 秘密のメッセージを隠す最も簡単な方法の一つは大文字を使うことです。これらの秘密のメッセージを探してみましょう。

あなたはテキストの塊を与えられます。一つの単語のすべての大文字を順番に合わせるとテキストが現れます。

例えば text = "How are you? Eh, ok. Low or Lower? Ohhh.", の時、大文字を集めると"HELLO"というメッセージが得られます。

入力 文字列(unicode) のテキスト

出力 秘密のメッセージの文字列か、空の文字列

find_message("How are you? Eh, ok. Low or Lower? Ohhh.") == "HELLO"

find_message("hello world!") == ""



どうやって使われるか これは文字列を扱う単純な練習(反復、認識、連結)です。

事前条件 0 < len(text) ≤ 1000
all(ch in string.printable for ch in text)


def find_message(text):
    """Find a secret message"""
    message = ""
    for x in text:
        if x.isupper(): message += x
        
    return message

if __name__ == '__main__':
    #These "asserts" using only for self-checking and not necessary for auto-testing
    assert find_message("How are you? Eh, ok. Low or Lower? Ohhh.") == "HELLO", "hello"
    assert find_message("hello world!") == "", "Nothing"
    assert find_message("HELLO WORLD!!!") == "HELLOWORLD", "Capitals"


これは大文字をさがして連結すれば良いから、forで文字列をまわしてisupper()で大文字かどうかを判定。
大文字であれば、空文字に対してどんどん連結する。
ひとつも大文字がなければ、空文字をそのまま返す。


一位の人の解答

find_message = lambda text: ''.join(filter(str.isupper, text))

なんと一行だ。
lambdaはわかるとして、joinとは?

str.join(iterable)

    イテラブル iterable 中の文字列を結合した文字列を返します。 iterable に bytes オブジェクトのような非文字列の値が存在するなら、 TypeError が送出されます。要素間のセパレータは、このメソッドを提供する文字列です。

文字列を結合して返すだけなのか。なるほど。

filterは?

filter(function, iterable)

    iterable の要素のうち function が真を返すものでイテレータを構築します。iterable はシーケンスか、反復をサポートするコンテナか、イテレータです。function が None なら、恒等関数を仮定します。すなわち、iterable の偽である要素がすべて除去されます。

    なお、filter(function, iterable) は、関数が None でなければジェネレータ式 (item for item in iterable if function(item)) と同等で、関数が None なら (item for item in iterable if item) と同等です。

    function が偽を返すような iterable の各要素を返す補完的関数は、 itertools.filterfalse() を参照下さい。

くりかえし可能なもの(今回は文字列)に、第一引数におく関数を適用して真だったものを返すのか。逆に偽の場合だけ返す関数もあるのかー。

じゃあ、filterで真のものだけ返してそれをjoinでくっつけるということをしているということか。

知らない組み込み関数がいろいろあるなー。