Edited at

言語処理100本ノック 2015 第1章: 準備運動(Python)

More than 3 years have passed since last update.

「言語処理100ノック 2015 第1章」解答例をのせています。

実装は、Python 2.7.1で行っています。

ご意見、ご指摘等あれば、よろしくお願いします。


言語処理100本ノックとは?

東北大学の乾・岡崎研究室が公開している問題集です。以下引用。

http://www.cl.ecei.tohoku.ac.jp/nlp100/


言語処理100本ノックは,実践的な課題に取り組みながら,プログラミング,データ分析,研究のスキルを楽しく習得することを目指した問題集です



第1章の内容

以下の内容を扱います。


テキストや文字列を扱う題材に取り組みながら,プログラミング言語のやや高度なトピックを復習します.

文字列, ユニコード, リスト型, 辞書型, 集合型, イテレータ, スライス, 乱数


この問題集自体Pythonで扱うことを想定しているため、この章では、Pythonの文法習得に役立つ問題中心となっていました。


00. 文字列の逆順


文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.


word = "stressed"

print word[::-1] #文字列の後ろからスライス

desserts


01. 「パタトクカシーー」


「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.


word = u"パタトクカシーー"

print word[::2]

パトカー


02. 「パトカー」+「タクシー」=「パタトクカシーー」


「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.


word1 = u"パトカー"

word2 = u"タクシー"
joined_word = [x + y for x , y in zip(word1, word2)]
print ''.join(joined_word)

パタトクカシーー


03. 円周率


"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.


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

text = text.translate(None, ".,") #「.」「,」を削除。
text = text.split() #str.split()は、デフォルトで空白で切り分け
num_char = [len(x) for x in text]
print text
print num_char

['Now', 'I', 'need', 'a', 'drink', 'alcoholic', 'of', 'course', 'after', 'the', 'heavy', 'lectures', 'involving', 'quantum', 'mechanics']

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]


04. 元素記号


"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文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.


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

text = text.translate(None, ".,")
text = text.split()
pos_dict = {}

for i, x in enumerate(text, start=1):
if i in [1, 5, 6, 7, 8, 9, 15, 16, 19]:
pos_dict[x[:1]] = i
else:
pos_dict[x[:2]] = i

# 辞書の中身を出現順にソートして表示 cf.http://qiita.com/xxthermidorxx/items/b546471c37b2f443f4c7
print sorted(pos_dict.items(), key=lambda x:x[1])

[('H', 1), ('He', 2), ('Li', 3), ('Be', 4), ('B', 5), ('C', 6), ('N', 7), ('O', 8), ('F', 9), ('Ne', 10), ('Na', 11), ('Mi', 12), ('Al', 13), ('Si', 14), ('P', 15), ('S', 16), ('Cl', 17), ('Ar', 18), ('K', 19), ('Ca', 20)]


05. n-gram


与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.


N_gramの解説 → https://kotobank.jp/word/N%E3%82%B0%E3%83%A9%E3%83%A0-1702302

def n_gram(char, n, mode = "word"):

"""
Args:
char:対象の文字列
n:Nの値
mode:単語で区切るなら「word」, 文字で区切るなら「char」.デフォルトはword
Return:
n_gram:N-gramで分解した結果
"""

n_gram = []
# 単語 or 文字で区切る
if (mode == "word"):
chars = char.split() # 単語で区切る
if (mode == "char"):
chars = char.translate(None, " ") # 文字で区切る

first_n = n
while n - 1 < len(chars):
n_gram.append(chars[n - first_n: n])
n += 1

return n_gram

# 問の回答
char = "I am an NLPer"
print "単語bi-gram"
words = n_gram(char, 2, "word")
print words

print "文字bi-gram"
chars = n_gram(char, 2, "char")
print chars

単語bi-gram

[['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
文字bi-gram
['Ia', 'am', 'ma', 'an', 'nN', 'NL', 'LP', 'Pe', 'er']


06. 集合


"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.


問題05のn_gram()を利用して解答しています。

char1 = "paraparaparadise"

char2 = "paragraph"

x = n_gram(char1, 2, "char")
y = n_gram(char2, 2, "char")
print "bi-gram"
print x
print y

bi-gram

['pa', 'ar', 'ra', 'ap', 'pa', 'ar', 'ra', 'ap', 'pa', 'ar', 'ra', 'ad', 'di', 'is', 'se']
['pa', 'ar', 'ra', 'ag', 'gr', 'ra', 'ap', 'ph']

# set型へ変換

x = set(n_gram(char1, 2, "char"))
y = set(n_gram(char2, 2, "char"))

print "和集合"

print x | y

和集合

set(['ad', 'ag', 'di', 'is', 'ap', 'pa', 'ra', 'ph', 'ar', 'se', 'gr'])

print "積集合"

print x & y

積集合

set(['ap', 'pa', 'ar', 'ra'])

print "差集合"

print x - y

差集合

set(['is', 'ad', 'se', 'di'])

print "'se'がXおよびYに含まれるかどうか"

print "se" in x
print "se" in y

'se'がXおよびYに含まれるかどうか

True
False


07. テンプレートによる文生成


引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.


def template(x, y, z):

"""
Args:
x:時間
y:「気温」などの事象
z:値
Return:
char:x時のyはz
"""

char = "%s時の%sは%s" % (x, y, z)
return char

print template(12, "気温", 22.4)

12時の気温は22.4


08. 暗号文


与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

英小文字ならば(219 - 文字コード)の文字に置換

その他の文字はそのまま出力

この関数を用い,英語のメッセージを暗号化・復号化せよ.


def cipher(text):

"""与えられた文の各文字を,以下の仕様で変換する関数

英小文字ならば(219 - 文字コード)の文字に置換
その他の文字はそのまま出力
"""
cipher_text = ""
for char in text:
if char.islower():
cipher_text += chr(219 - ord(char))
else:
cipher_text += char
return cipher_text

# 英語のメッセージを暗号化・復号化
text = "Hi He Lied Because Boron Could Not Oxidize Fluorine."
print("origin text : " + text)
cipher_text = cipher(text)
print("cipher text : " + cipher_text)
decode_text = cipher(cipher_text)
print("decode text : " + decode_text)

origin text : Hi He Lied Because Boron Could Not Oxidize Fluorine.

cipher text : Hr Hv Lrvw Bvxzfhv Blilm Clfow Nlg Ocrwrav Foflirmv.
decode text : Hi He Lied Because Boron Could Not Oxidize Fluorine.