参考元
Q.1 Stressful Subject
休みとるぜ!だから休暇中のストレスを避けるためにA stressful subjectを含むメール以外無視する関数を作ってくれとのこと。
A stressful subjectを認識する関数を作るために、
A stressful subjectとは、
・メールが全部大文字のとき
・!が3つ以上連続した時
・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:
is_stressful("Hi") == False
is_stressful("I neeed HELP") == True
自分の回答:
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/
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")]])
3行?実質1行じゃねぇか。頭おかしいよ。
同じ問題を俺たちは解いてるんだよな?というレベル。理解しようと試みる。
今日はこの意味ふみこを整理しよう。
分解と整理をすればなんとかなると信じろ
lambda式
lambda
はPyhtonにおいて無名関数と呼ばれる。ラムダと読む。
その名の通り、名前のない関数を作成できる。(名前をつけることもできる)
使い捨てで、二度と使わない関数とかに気軽に使える。都合のいい女or男or神みたいなやつ。
参考元:https://www.lifewithpython.com/2013/01/python-anonymous-function-lambda.html
lambda式のフォーマットはa_function名 = lambda x:y
。x
が引数。y
が戻り値。
a_function = lambda x: x **2
a_function(10) #100
a_function(3) #9
上記lambda式は
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に関するのあったから、そっち見てから深く切り込んでいけ。
def any(iterable):
for element in iterable:
if element:
return True
return False
と同じ意味。以下例
>>> 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で返す。
他人コードをもう一回ちょっと分解してみてみる。
*[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つの場合は、辞書をデフォルト引数」「キーワード可変長引数」に対して展開する。
>>> 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/
>>> 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,2,3],[4,5,6],[7,8,9]がでてくる。3つのリストが
zip()
にはいったてこと。 この3つのリストをzip()で回したことになる。各リストのindex0番目から順番にa,b,cに入る。故に最初は1,4,7が出力される。
最終整理
また本題に戻って、
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")]])
-
any()
のなかのjoin()
の中から順番に見ていく。join()
の中で変数cにおけるリスト内包表記が行われる。 - subject.upper()。subjectはlamda式で定義されるis_stressful関数の引数。
lambda subject:any()
のsubject。この引数subjectをupper()
で大文字にしている。このsubjectの大文字化された文字列は、zip()
で使われる1つめのシーケンス。 - 次の
subject[1:]+""
は引数subjectをindex1からindexの末尾までスライスして、" "を文字列結合させたもの。zip()
で使われる2つめのシーケンス。 - 上記シーケンス2つがzip()の複数のシーケンス。2つのシーケンスは同じ要素数。ゆえに
for c, d in zip()
において、cはsubject[0]から順番に返し。dはsubject[1]から末尾" "までを順番に返す。 - (c ~ c != d)内部は内包表記何じゃないか?と考える。理由は変数cが前にでて新しいリストをfor文の値によって作ろうとしている。また、if文でその値を選別しようとしているよう見えるから。
- 変数cにおけるfor文の後のif節を見る。変数c(subject[0]からのシーケンス)がAからZまでのどれかのアルファベットであり、且つ c != dの2つのif節条件を満たした時、cの値のリストが作られる。…はず。
- なぜif節で
A <= c <= Z
かつc != d
としたのかは、red wordsが"Help","ASAP","URGENT"であるからだ。どれもA-Zのアルファベット(if節の1つめの条件)で、かつ同じアルファベットが連続していない単語である(if節の2つめの条件)。 -
"".join()
で5.のリスト内包表記によって作られた変数cの値からなるリストが結合される。 - そしてもう1回変数rにおけるリスト内包表記が行われる。
*[r in "".join() for r in ("HELP", "ASAP", "URGENT")]
。ここで俺が最初見て混乱したのは、1回目のinと、2回目のinがの仕事が違うことだ。1回目のinはTrue, Falseの返す検索のinだ。2回めのfor文内のinは範囲を示す。 つまり変数rは"HELP", "ASAP", "URGENT"を1つずつ取り出した値を一時格納する。それは文頭のrとして使われる。最終的にr in "".join()
となる。"".join()
の戻り値は変数cの値のリストが結合したものだ。それらの中に"HELP", "ASAP", "URGENT"があるかをin
で調べている。 -
r in cの文字結合
はリストの形で返ってくる。そのリストは3つの要素を持つ。"HELP","ASAP","URGENT"と3つだからだ。True,Falseのどちらかが格納されたリストだ。このリストは大きなリストのなかの1つの値だ(元のコードをみると分かる)。ゆえに新しく作られたリストを*[]で展開し、大きなリストの要素として格納し直している。any()
の引数用に,1つのリストに要素を入れ直していると言い帰れる。 -
any()
が値を評価する。シーケンスの中のそれぞれのデータ型において。ブール値によるふるまいでTrueをかえす値が1つでも存在すれ場合。このany()
はTrueを戻り値に返す。逆にFalseを返すなら、1つもTrueに振る舞うデータ型がなかったてことだ。
最後にもう1回同じコード。
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は神。
まとめ と 自分で再作成
何かかっこいい文を自分でも分かるようダサいコードにするとこうなる
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
-
str.endswith()
は語尾から文字列を調べてくれる関数。str.stratswith()
は文頭から文字列を調べてくれる。 -
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のリストが作られる。cの値はfor文から受け取り。if節で選別される。
4.1 ここもリスト内包表記。作られたリストは、any()
関数の引数として使われる。各データ型のブール値の振る舞いでTrueが1つでも存在すれば、Trueを戻り値にする。
4.2 []は可変長の引数を展開する。言い換えると、,で区切られた形の引数として扱うことができるようになる。
例1) 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な引数1つしかとることができないっぽい。any(1,2,3,4,5)とかerrorっぽいぞ。
以上。まとめおわり。
問題は1つしか進まなかったし、わけわからん他人の解答見て頭爆発してフリーズしたけど僕は元気です。
終わりに。
毎日投稿\(^o^)/オワタ。ブドウ味のアメがおいしい。ブドウ好き。