言語処理100本ノック 第1章 in Python

  • 43
    いいね
  • 14
    コメント
この記事は最終更新日から1年以上が経過しています。

言語処理100本ノックの第一章です。Ubuntu 14.04上でPython 3.4.1を使って行いました。
ほとんどPythonの練習のためにやっているので、より良い書き方あったらコメントで教えていただけると嬉しいです
(今回はココを参考にしたのでかなり良くなったと思います。)

問題文、ソースコード、実行結果の順に載せてます。

00. 文字列の逆順

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

00.py
word="stressed"
print(word[::-1])
desserts

スライス便利。 :P

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

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

01.py
string="パタトクカシーー"
print(string[::2])
パトカー

スライス便利。 :P

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

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

02.py
word1="パトカー"
word2="タクシー"
ans="".join([c1+c2 for c1,c2 in zip(word1,word2)])
print(ans)
パタトクカシーー

joinは"-".join(list)とした時、-を区切り文字としてlistの要素を連結してstr型に、zipは複数のシーケンスオブジェクトを同時に取り出す関数。

03. 円周率

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

03.py
sentence="Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
count=[len(word.strip(".,")) for word in sentence.split()]
print(count)
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

splitは文字列を指定の文字ごとに区切ってリストにする関数、stripは文字列から指定の文字を削除する関数
=>stripは両端にある文字を削除する関数でした。

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

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

#単語に分解
words=[word.strip(".,") for word in sentence.split()]

#先頭の1文字(または2文字)と、その単語のインデックスを対応付ける辞書を作成
link={}
for i,v in enumerate(words,1):
    length=1 if i in [1,5,6,7,8,9,15,16,19] else 2
    link.update({v[:length]:i})
print(link)
{'P': 15, 'S': 16, 'Cl': 17, 'Al': 13, 'He': 2, 'C': 6, 'Mi': 12, 'Na': 11, 'F': 9, 'Si': 14, 'Ar': 18, 'Ca': 20, 'H': 1, 'Li': 3, 'N': 7, 'B': 5, 'Ne': 10, 'Be': 4, 'K': 19, 'O': 8}

enumurateはリストのインデックスと要素を同時に取り出せる関数。zipと併せてfor文でかなり使いそう。

よりスマートな別解
sentence ="Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
#先頭の1文字(または2文字)と、その単語のインデックスを対応付ける辞書を作成
link = {w[:2-(i in (1,5,6,7,8,9,15,16,19))]:i for i,w in enumerate(sentence.split(),1)}
print(link)

05. n-gram

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

05.py
sentence="I am an NLPer"

#文字bi-gram
charGram=[sentence[i:i+2] for i in range(len(sentence)-1)]

#単語bi-gram
words=[word.strip(".,") for word in sentence.split()] 
wordGram=["-".join(words[i:i+2]) for i in range(len(words)-1)]

print(charGram)
print(wordGram)
['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
['I-am', 'am-an', 'an-NLPer']

06. 集合

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

06.py
word1="paraparaparadise"
word2="paragraph"
#単語bi-gram
X=set([word1[i:i+2] for i in range(len(word1)-1)])
Y=set([word2[i:i+2] for i in range(len(word2)-1)])

print("X="+str(X))
print("Y="+str(Y))
print ("和集合:"+str(X|Y))
print ("差集合:"+str(X-Y))
print ("積集合:"+str(X&Y))
if 'se' in X:
    print( "'se'はXに含まれるよ" )
if 'se' in Y:
    print( "'se'はYに含まれるよ" )
X={'ap', 'ad', 'di', 'ar', 'is', 'pa', 'ra', 'se'}
Y={'ap', 'ar', 'gr', 'ph', 'ag', 'pa', 'ra'}
和集合:{'ap', 'ad', 'di', 'ar', 'gr', 'is', 'ph', 'ag', 'pa', 'ra', 'se'}
差集合:{'ad', 'di', 'se', 'is'}
積集合:{'ar', 'ap', 'pa', 'ra'}
'se'はXに含まれるよ

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

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

07.py
def mkStr(x,y,z):
    return "{0}時の{1}は{2}".format(x,y,z)

if __name__=="__main__":
    print(mkStr(12,"気温",22.4))
12時の気温は22.4

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
- 英小文字ならば(219 - 文字コード)の文字に置換
- その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.

08.py
def cipher(string):
    return ''.join(chr(219-ord(c)) if 'a'<=c<='z' else c for c in string)

if __name__=="__main__":
    sentence="Hello, world!"
    ciphertext=cipher(sentence)
    print(sentence)
    print(ciphertext)
    print(cipher(ciphertext)) 
Hello, world!
Hvool, dliow!
Hello, world!

(219-文字コード)の操作で暗号化も復号化もできます。すなわち、2回やれば帰って来る。

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.

09.py
import sys,random,itertools

sentence="I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind."

#文を単語に分解
words=[word.strip(".,") for word in sentence.split()]

for idx,word in enumerate(words):
    ret=""
    L=len(word)
    #4文字以下はなにもしない
    if 4<L:
        #最初は確定
        ret+=word[0]
        #並べ替えパターンを全てリストに
        perm=list(itertools.permutations(list(word[1:L-1]),L-2))
        #乱数生成
        rnd=random.randint(0,len(perm)-1)
        #並びのなかからランダムにピック
        ret+="".join(perm[rnd])
        #最後も確定
        ret+=word[L-1]
        words[idx]=ret
print(words)
['I', "cldo'unt", 'bvieele', 'that', 'I', 'colud', 'aclulaty', 'udartsnend', 'what', 'I', 'was', 'ridneag', ':', 'the', 'pmeohnnael', 'pwoer', 'of', 'the', 'hmaun', 'mind']
よりスマートな別解
from random import shuffle

sentence="I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind."
def typo(word):
    middle=list(word[1:-1])
    shuffle(middle)
    return word[0]+''.join(middle)+word[-1]
print(' '.join(typo(w) if len(w)>4 else w for w in sentence.split()))

':'が気になるけど、これstrip()に入れて消しても空の要素がひとつ残ってわざわざ消すのだるいので無視笑
ちなみに、

Typoglycemiaとは、単語を構成する文字を並べ替えても、最初と最後の文字が合っていれば読めてしまう現象のことである。
Typoglycemia - ニコニコ大百科

らしいです。

以上です!
一周してまた見なおしたり、他の方のコードを見ると洗練されてくる感じが楽しいです。00,01あたりも最初はスライスじゃなくてfor文回してましたから…。
段々pythonらしい書き方みたいなのが少しづつわかってきて嬉しいのですが、時間がかかりました…。これで準備運動とか大丈夫か…^^;