雑記帳(@watagasi_)

【言語処理100本ノック】04. 元素記号【python】

03でだらだらプログラム書いていたんですけど↓

watagassy.hatenablog.com

なんと3行で済むみたいですね。はい。

 

s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

count = [len(i.strip(",.")) for i in s.split()]

print(count) 

参考:Python初心者が少しずつ言語処理100本ノックを頑張るエントリー - KAZUMA IEIRI

 

プログラム書きなれていないとこうなるわけです。

 

というかfor文ってこんな書き方できたんですね~。

 

これを「count = [len(i) for i in re.split("\W+",s).pop()]」とすれば自分の書いたプログラムだな~…と思ったら違いますね。

 

pop()は配列の最後の値を削除した後、削除した値を返すのであって、元の配列を返してくれるわけではありません。

 

じゃあ「re.split("\W+",s).remove("")」か?と思いましたが、これも違います。remove()は削除をしてくれるだけで、やっぱり元の配列を返してくれるわけではありません。

 

配列の要素を削除した後の配列を返してくれる関数は、自分の探した範囲だとないみたいで、多分あったとしてもメジャーじゃない、ってことかな?

 

ということは、re.split()よりもsplit()とstrip(",.")を重ねる方が、単語分解においては賢いのでしょうか。勉強が必要ですね。

 

 

ではお題を見ていきましょう。

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

 

 今回はいくつかコードを書いてみたので、それぞれ見ていこうと思います。

 

まず最初に書いたのがこれ。

 

import re
test_str = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

result = re.split("\W+",test_str)
result.pop()
char_list = []

for
i in range(len(result)):
char_list.append(list(result[i]))

dic1 = {}
char_listz = []

for
i in range(len(char_list)):
if i == 0 or i ==4 or i ==5 or i ==6 or i ==7 or i ==8 or i ==14 or i ==15:
dic1[char_list[i][0]] = i+1
else:
char_listz.append(char_list[i][0])
char_listz.append(char_list[i][1])
dic1["".join(char_listz)] = i+1
char_listz = []
print(dic1)

 

う~ん、汚すぎる。

 

上記の反省を生かす前なのでこんな感じです。pop()は前回に引き続き余計な感じで、変数もいちいち宣言しすぎですね。

 

これ、いったん文を単語に分解して、さらに単語を文字に分解したリストを作って、そしてそのリストの番号が指定されたものだったら、単語を2文字append、そうでなかったら1文字appendとかやってるんですけど。

 

さっきの方のブログ見るとまあひどいですねこれ。pythonの文字の扱いやすさを全くわかってない。あとfor文あたりの考え方もC言語のまんまっぽい。

 

というわけで反省して書き直したのがこちら。

s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

dic = {}
for i,word in enumerate(s.split(), 1):
if i in (1, 5, 6, 7, 8, 9, 15, 16, 19):
dic[word.strip(".")[:1]] = i
else:
dic[word.strip(".")[:2]] = i

print(dic)

まあきれいになりましたね。いい感じではないでしょうか。

 

 

 

ここで上記のブログの方がどう書いているか見てみました。

s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
dic = {word[:2-(i in (1,5,6,7,8,9,15,16,19))]:i for i, word in enumerate(s.replace(".", "").split(), 1)}
print(dic)

 

????????

 

一瞬意味が分かりませんでしたがよく見ていきます。

 

一応for以下は自分とだいたい同じなのでわかります。それ以外ですね。

 

まず第一の問題は[:2-(i in (1,5,6,7,8,9,15,16,19))]の部分です。最初見たとき「なんでこれで2文字と1文字のスライスが区別できるんだ????」って思いました。

 

 

そしていくつか試してみて合点がいきました。

 

突然ですが、pythonで「print(5-True)」と入力するとどうなるでしょうか?

 

答えは「4」が返ってきます。[:2-(i in (1,5,6,7,8,9,15,16,19))]の部分はこれを利用しているんですね。

 

「i in (1,5,6,7,8,9,15,16,19)」は、iが該当の数字であったときTrueを返します。

 

で、条件に合ったときに「2-True」が計算されて「1」になり、文字列の先頭1文字だけ取り出せるんですね。

 

こういうのってプログラミング慣れてる人は普通なんでしょうか?「python初心者とか言っときながら自分とスタート地点全然ちげーじゃねえか」ってキレそうになりました。

 

 

そう、そして第二の問題はディクショナリへの要素の追加が「dic ={a:b}」という形で行われていたことです。

 

ディクショナリに要素を追加するときは「dic[a]=b」みたいに書けってどこにでも書いてあるんですが、この人はどこから「for文を回せばdic ={a:b}の形式で書ける」と知ったのでしょう。絶対初心者じゃないですよね。

 

 

とりあえずpythonは短くスマートに書こうと思えばいくらでもそのようにできるという学びは得たので、次からはもっとペース挙げてやっていきましょう。