python Checkio.org  Stage: SendGridを解く その1(ほぼ他人の解答への理解を試行錯誤)

##参考元
https://py.checkio.org/mission/stressful-subject/


##Q.1 Stressful Subject
休みとるぜ！だから休暇中のストレスを避けるためにA stressful subjectを含むメール以外無視する関数を作ってくれとのこと。
A stressful subjectを認識する関数を作るために、
A stressful subjectとは、
・メールが全部大文字のとき
・!が３つ以上連続した時
・help, asap, urgentのいずれかを含んだ時。こいつらをred wordsと定義する
・ "HELP", "help", "HeLp", "H!E!L!P!", "H-E-L-P", even in a very loooong way "HHHEEEEEEEEELLP"のような違った形式でもred wordsとみなす。

Input:
 Subject lines as a String
Output:
 Boolean
Precondition: 
 subject can be up to 100 letters
Example:

```py
is_stressful("Hi") == False
is_stressful("I neeed HELP") == True
```

自分の回答：

```py
def redword(word):
    for i in range(len(word)):
        if word[i] == "help" or word[i] == "asap" or word[i] == "urgent":
            return True
        elif "h" in word[i] and "e" in word[i] and "l" in word[i] and "p" in word[i]:
            return True
        elif "a" in word[i] and "s" in word[i] and "p" in word[i]:
            return True
        elif "u" in word[i] and "r" in word[i] and "g" in word[i] and "e" in word[i] and "n" in word[i] and "t"in word[i]:
            return True
    return False

def is_stressful(subj):
    """
        recoognise stressful subject
    """
    print(subj.isupper())
    if subj.isupper():
        return True
    else:
        word = subj.lower().split(" ")
        if word[-1].count("!") >= 3:    #check !!!
            #if [i for i in word[-1] if i == "!")]
            if (word[-1][-1] + word[-1][-2] + word[-1][-3]) == "!!!":
                return True
            else:   #red word
                return redword(word)
        else:   #red word
            return redword(word)
```

inで強引に調べた。正直ものすごいガバガバ。h,e,l,pの順番を気にしてないから、こいつらを含む他の単語でもTrueを返してしまう。問題のcheckは通ってしまったから載せるが…。関門ガバガバすぎない？
↓↓↓↓↓

##他人のcreativeなAnswer
試しに人のコードを見たらなんかスゲェ。
引用元：
https://py.checkio.org/mission/stressful-subject/publications/review/puzzle/

```py
is_stressful = lambda subject: any([subject.endswith("!!!"), subject == subject.upper(),
                                   *[r in "".join(c for c, d in zip(subject.upper(), subject[1:] + " ")
                                     if "A" <= c <= "Z" and c != d) for r in ("HELP", "ASAP", "URGENT")]])  
```

３行？実質１行じゃねぇか。頭おかしいよ。
同じ問題を俺たちは解いてるんだよな？というレベル。理解しようと試みる。
今日はこの意味ふみこを整理しよう。

###分解と整理をすればなんとかなると信じろ
####lambda式
`lambda`はPyhtonにおいて無名関数と呼ばれる。ラムダと読む。
その名の通り、名前のない関数を作成できる。(名前をつけることもできる）
使い捨てで、二度と使わない関数とかに気軽に使える。都合のいい女or男or神みたいなやつ。
参考元：https://www.lifewithpython.com/2013/01/python-anonymous-function-lambda.html
lambda式のフォーマットは`a_function名 = lambda x:y`。`x`が引数。`y`が戻り値。

```py
a_function = lambda x: x **2
a_function(10) #100
a_function(3) #9
```

上記lambda式は

```py
def a_function(x):
    return x ** 2
```
と書くのと同じ。
以下実践例:lambda　(ラムダ）と`sorted()`
####`sorted()`
`sorted()`　ohこんな便利な関数があるのを知らんかった。与えられたリストをソートしてくれる関数。元リストは変更せず、並び替えたリストのコピーを返す。任意のイテラブルを受け付ける。
参考元：https://docs.python.jp/3/howto/sorting.html　
上記は`sorted()`に関する公式ドキュメント。
 `sorted()`に関連して`key`パラメータがある。keyパラメータは単一の引数をとり、ソートに利用される。keyの指定にはソートに使われるkeyを返す必要がある。

```
>>> student_tuples = [
...     ('john', 'A', 15),
...     ('jane', 'B', 12),
...     ('dave', 'B', 10),
... ]
>>> sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
```
lambda直後のstudentは引数名だからなんでもいい。:の次のstudent[2]はkeyに引数studentのstudent[2]を戻り値として渡している。この渡した値を基準に`sotred()`はソートを行う。この場合は、数字を小さい→大きい順。昇順。


####`any()`
`any(iterable)`iterableのいずれかの要素が真ならば`True`を返す。
iterableが空なら`False`を返す。iterableってなんだよ…。リストとか、
iterableに関する公式ドキュメント：https://docs.python.jp/3/glossary.html#term-iterable　
iterableだけ全部英語。えぇ…。
要約すると、all sequence types (such as list, str, and tuple)はiterable。かつsome non-sequence types like dict, file objectsもiterable。以下略。いまは深く突っ込みたくない。沼やぞ沼。謎が謎を呼ぶぞ。Dive Into Pythonの章にiterableに関するのあったから、そっち見てから深く切り込んでいけ。

```py
def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
```
と同じ意味。以下例

```py
>>> any([False, ""])
False
>>> any([1, False, ""])
True
```
シーケンス内のいずれかの要素が真ならば、Trueを返す。各データ型におけるbool値の振る舞いを参照してる希ガス。Dive Into Pyhtonで軽く触ったけどあやふやですわ。
bool値のFalseのみが`any()`の中にあってもFalse。

参考元：
https://docs.python.jp/3/library/functions.html?highlight=any#any

####`endswith()`
`endswith()`関数は文字列の末尾から文字列を検索できる。オプションで開始位置, 終了位置の指定をできる。Bool値を戻す。
`endswiht("!!!")`で末尾に"!!!"があるか調べている。

`str.upper()`strを大文字にする。`str.isupper()`でstrが大文字化判定。True,Falseで返す。


他人コードをもう一回ちょっと分解してみてみる。

```py
 *[r in "".join(c for c, d in zip(subject.upper(), subject[1:] + " ")　if "A" <= c <= "Z" and c != d) for r in ("HELP", "ASAP", "URGENT")]]
```
パット見じゃわけわかめ。

####`*[]`　可変長引数
まず`*[]`が調べても分からねぇ。可変長引数？
参考元：
http://note.crohaco.net/2014/python-argument-intro/
`*`は複数の値を持ちうる引数を展開する。あるいは、引数が複数の値を受け取れるようにするとのこと(可変長引数)。
`*`はリストやタプルの前につくと、リストやタプルを通常引数、デフォルト引数、可変長引数に対して展開します。←ってどういう意味やねん。リストやタプルとかを、リストやタプルじゃない形式で使えるようにするってことか？
**と2つの場合は、辞書をデフォルト引数」「キーワード可変長引数」に対して展開する。

```py
>>> def apply_test(normal, default=1, *args, **kwargs):
...     print normal
...     print default
...     print args
...     print kwargs
... 
>>> args = [1, 2, 3, 4]
>>> # *での展開は「通常引数」「デフォルト引数」「可変長引数」に対して行われる
>>> apply_test(*args)
1
2
(3, 4)  #可変長引数でうけとったからタプル。
{}
```
 apply_test(*args)において、*argsが展開（＝リストの各要素ごとにバラバラになる）されて、normalにargs[0],　defaultにargs[1],　仮引数argsにargs[2]と[3]が各々格納された。args[2],args[3]がタプルに格納された理由は↓
 apply_test()の定義において、仮引数として`*args`が使われてる。このargs前についた`*`が、引数を可変長引数に変化させる。`引数の前に*`をつけると、受け取った引数を順番にタプルに格納する。
咀嚼して消化するのにちょっと時間がかかりますん。
今回の場合、*[]はどういう扱いなんだよぉ！？←つまり、引数の展開の*[]

####`join()`
`str.join()`関数。strを区切り文字として使って、引数のシーケンスを連結する。
`"区切り文字".join(リスト)`の形式

```
>>> "".join(["a","b","c","命"]
'abc命'
>>> "siri".join(["a","b","c","乳"])
'asiribsiricsiri乳'
```
本題に戻って、join()の中身
`c for c, d in zip(subject.upper(), subject[1:] + " "`を見る。
多分これは昨日やったタプル内包表記???違うかも。

`zip()`:
`zip()`複数のシーケンスをまとめてループさせるのに使う。要素数が違う場合は一番少ないものに合わせてループを行う。
参考元：https://python.civic-apps.com/zip-enumerate/

```py
>>> l1 = [1,2,3]
>>> l2 = [4,5,6]
>>> for (a, b) in zip(l1, l2):　#l1,l2を同時ループ
...   print(a, b)
... 
1 4
2 5
3 6
>>> list3 = ["oppa", "siri"]
>>> for (a, b) in zip(l1, list3):　　#要素数の少ないlist3に合わさる
...  print(a, b)
... 
1 oppa
2 siri
>>> l4 = [
... [1,2,3],
... [4,5,6],
... [7,8,9]]
>>> l4
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> for (a,b,c) in zip(*l4):    ①
...   print(a,b,c)
... 
1 4 7
2 5 8
3 6 9
```

1. コレ最後は何が起こってんの？あー。まず*[]でリストが分解される。
リストが分解されて[1,2,3],[4,5,6],[7,8,9]がでてくる。３つのリストが`zip()`にはいったてこと。
この３つのリストをzip()で回したことになる。各リストのindex0番目から順番にa,b,cに入る。故に最初は1,4,7が出力される。

####最終整理
また本題に戻って、

```py
any(
*[r in "".join(c for c, d in zip(subject.upper(), subject[1:] + " ") if "A" <= c <= "Z" and c != d) for r in ("HELP", "ASAP", "URGENT")]])
```
 0. `any()`のなかの`join()`の中から順番に見ていく。`join()`の中で変数ｃにおけるリスト内包表記が行われる。
 1. subject.upper()。subjectはlamda式で定義されるis_stressful関数の引数。`lambda subject:any()`のsubject。この引数subjectを`upper()`で大文字にしている。このsubjectの大文字化された文字列は、`zip()`で使われる１つめのシーケンス。
 2. 次の`subject[1:]+""`は引数subjectをindex1からindexの末尾までスライスして、"　"を文字列結合させたもの。`zip()`で使われる2つめのシーケンス。
 3. 上記シーケンス２つがzip()の複数のシーケンス。２つのシーケンスは同じ要素数。ゆえに`for c, d in zip()`において、cはsubject[0]から順番に返し。dはsubject[1]から末尾" "までを順番に返す。
 4. (c ~ c != d)内部は内包表記何じゃないか？と考える。理由は変数cが前にでて新しいリストをfor文の値によって作ろうとしている。また、if文でその値を選別しようとしているよう見えるから。
 5. 変数cにおけるfor文の後のif節を見る。変数c(subject[0]からのシーケンス)がAからZまでのどれかのアルファベットであり、且つ c != dの２つのif節条件を満たした時、cの値のリストが作られる。…はず。
 6. なぜif節で`A <= c <= Z`かつ`c != d`としたのかは、red wordsが"Help","ASAP","URGENT"であるからだ。どれもA-Zのアルファベット(if節の1つめの条件)で、かつ同じアルファベットが連続していない単語である(if節の２つめの条件)。
 7. `"".join()`で5.のリスト内包表記によって作られた変数cの値からなるリストが結合される。
 8. そしてもう１回変数rにおけるリスト内包表記が行われる。`*[r in "".join() for　r in ("HELP", "ASAP", "URGENT")]`。ここで俺が最初見て混乱したのは、1回目のinと、２回目のinがの仕事が違うことだ。1回目のinはTrue, Falseの返す検索のinだ。２回めのfor文内のinは範囲を示す。
つまり変数rは"HELP", "ASAP", "URGENT"を１つずつ取り出した値を一時格納する。それは文頭のrとして使われる。最終的に`r in "".join()`となる。`"".join()`の戻り値は変数ｃの値のリストが結合したものだ。それらの中に"HELP", "ASAP", "URGENT"があるかを`in`で調べている。
 9. `r in cの文字結合`はリストの形で返ってくる。そのリストは３つの要素を持つ。"HELP","ASAP","URGENT"と３つだからだ。True,Falseのどちらかが格納されたリストだ。このリストは大きなリストのなかの１つの値だ(元のコードをみると分かる)。ゆえに新しく作られたリストを*[]で展開し、大きなリストの要素として格納し直している。`any()`の引数用に,１つのリストに要素を入れ直していると言い帰れる。
 10. `any()`が値を評価する。シーケンスの中のそれぞれのデータ型において。ブール値によるふるまいでTrueをかえす値が１つでも存在すれ場合。この`any()`はTrueを戻り値に返す。逆にFalseを返すなら、１つもTrueに振る舞うデータ型がなかったてことだ。

最後にもう１回同じコード。

```py
is_stressful = lambda subject: any([subject.endswith("!!!"), subject == subject.upper(),
                                   *[r in "".join(c for c, d in zip(subject.upper(), subject[1:] + " ")
                                     if "A" <= c <= "Z" and c != d) for r in ("HELP", "ASAP", "URGENT")]])   
```

以上自分用のむしゃむしゃそしゃく解説終わり。
###わかったこと
・問題を解決する具体的な案がかっこいいほどコードもかっこよくなる
・メソッドと関数を知るのにhttps://docs.python.jp/3/のPython3.6.3
ドキュメントがやべぇ役に立つ
・googleは神。

###まとめ　と　自分で再作成
何かかっこいい文を自分でも分かるようダサいコードにするとこうなる

```py

def is_stressful(subject):
    if subject.endswith("!!!"):    ①
        return True
    l1 = [c for c, d in zip(subject.upper(), subject[1:]+" ") if "A" <= c <= "Z" and c != d]　    ③
    red_words = ["HELP", "ASAP", "URGENT"]
    if any([subject.isupper(), *[r in "".join(l1) for r in red_words]]):     ④#*[]を無理矢理でも使いたかった。any()の外にsubject.upper()を出せば。r直前の*はいらない。
        return True
    else:
        return False
```
1. `str.endswith()`は語尾から文字列を調べてくれる関数。`str.stratswith()`は文頭から文字列を調べてくれる。
2. `str.isupper()`はアルファベットの大文字か調べてくれる。`str.upper()`なら大文字にしてくれる。`if subject == subject.upper()`としてもいい。

3.1 リスト内包表記。昨日(一昨日)dive into python第3章でやった。最初のcに対していくら複雑な関数でも適用することできる。変数cを引数に使われた関数の戻り値のリストを作成してくれる。使うとコードがスッキリと圧縮される。if節をfor文の後ろにつけて、作られるリストへ格納される値を選別できる。

3.2 `zip()`はfor文を複数のシーケンスではしらせることができる。要素数が違う場合は、少ない方に合わせてforループを行う。

3.3 リスト内包表記によって、cのリストが作られる。ｃの値はfor文から受け取り。if節で選別される。

4.1 ここもリスト内包表記。作られたリストは、`any()`関数の引数として使われる。各データ型のブール値の振る舞いでTrueが１つでも存在すれば、Trueを戻り値にする。

4.2 *[]は可変長の引数を展開する。言い換えると、,で区切られた形の引数として扱うことができるようになる。
例１) `any([1,2,3,4,5])`のままならリストとして扱われる。
例2) `any(*[1,2,3,4,5])`なら`any(1,2,3,4,5)`と等価。←これエラー出るけど。

4.3 リスト内包表記の変数に対して、あらゆる関数を使うことが出来る。ここでは`in`。l1の結合された文字列に対して、リストred_wordsの中身でin検索を書けている。その結果はリストとして新しく作成される。*[]ゆえに展開され、`any()`の引数として使われる。
↑
これ`any()`に関して間違ったこと書いてるぞ。`any()`はiterableな引数１つしかとることができないっぽい。any(1,2,3,4,5)とかerrorっぽいぞ。

以上。まとめおわり。
問題は１つしか進まなかったし、わけわからん他人の解答見て頭爆発してフリーズしたけど僕は元気です。


##終わりに。
毎日投稿＼(^o^)／ｵﾜﾀ。ブドウ味のアメがおいしい。ブドウ好き。