言語処理100本ノックの第一章です。
環境はWindows10,python3.6.0です。こちらを参考にさせていただきました。
00.文字列の逆順
"stressed"の文字を逆に並べた文字列を得よ。
# coding: utf-8
target = "stressed"
new_target = target[::-1]
print(new_target)
desserts
default は step が正の時0,8、負の時8、0
01.「パタトクカシーー」
「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.
# coding: utf-8
word = "パタトクカシーー"
new_word = word[::2]
print(new_word)
パトカー
uを忘れないようにする。
02.「パトカー」+「タクシー」 = 「パタトクカシー」
「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ
# cording utf-8
word1 = u"パトカー"
word2 = u"タクシー"
mix_word = ""
for w1,w2 in zip (word1,word2):
mix_word += w1 + w2
print(mix_word)
パタトクカシーー
- 長い方に合わせるzip
import itertools
target1 = '12345'
target2 = 'abc'
zipped = itertools.zip_longest(target1,target2)
print(list(zipped))
[('1', 'a'), ('2', 'b'), ('3', 'c'), ('4', None), ('5', None)]
- 値をNone以外にする
import itertools
target1 = '12345'
target2 = 'abc'
zipped = itertools.zip_longest(target1,target2,fillvalue = False )
print(list(zipped))
[('1', 'a'), ('2', 'b'), ('3', 'c'), ('4', False), ('5', False)]
- もう一回zip()すると元に戻る。
import itertools
target1 = '12345'
target2 = 'abc'
zipped = itertools.zip_longest(target1,target2,fillvalue = False )
zipped_list = list(zipped)
zizipped = zip(zipped_list[0],zipped_list[1],zipped_list[2],zipped_list[3],zipped_list[4])
print(list(zizipped))
[('1', '2', '3', '4', '5'), ('a', 'b', 'c', False, False)]
- *を使ったver
import itertools
target1 = '12345'
target2 = 'abc'
zipped = itertools.zip_longest(target1,target2,fillvalue = False )
zipped_list = list(zipped)
zizipped = zip(*zipped_list)
print(list(zizipped))
[('1', '2', '3', '4', '5'), ('a', 'b', 'c', False, False)]
03.円周率
"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.
words = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
result = []
new_words = words.translate(str.maketrans("","",",."))
for word in new_words.split(' '):
word_length = len(word)
result.append(word_length)
print(result)
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
strip は両端から指定の文字列を削除する。
str.translate(str.maketrans("","",".,"))
第1→第2 第三因数は削除したい文字列。
- 美しい解答
words = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
result = [len(word.strip(",.")) for word in words.split(" ")]
print(result)
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
import re
words = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
result = [len(word) for word in (re.sub(r"[,.]","",words).split(" "))]
print(result)
[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文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.
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()]
dic = {word[0]:words.index(word) + 1 for word in words if words.index(word) in (0,4,5,6,7,8,14,15,18)}
dic.update({word[:2]:words.index(word) + 1 for word in words if words.index(word) not in (0,4,5,6,7,8,14,15,18)})
print(dic)
{'H': 1, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'P': 15, 'S': 16, 'K': 19, 'He': 2, 'Li': 3, 'Be': 4, 'Ne': 10, 'Na': 11, 'Mi': 12, 'Al': 13, 'Si': 14, 'Cl': 17, 'Ar': 18, 'Ca': 20}
- 別解 1
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()]
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)
{'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}
- 別解 2
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)
{'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}
Boolean値の利用 True = 1 False= 0
05. n-gram
与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
import re
sentence_string = "I am an NLPer"
sentence_list = sentence_string.split()
def n_gram(sequence,n):
u"""文字列で渡されたとき文字bi-gram,リストとして渡されたとき単語bi-gramとして扱われる。
"""
result = []
if isinstance(sequence,str):
sequence = list(re.sub("[,. ]","",sequence))
for i in range(len(sequence)- n+1):
result.append('-'.join(sequence[i:i+n]))
return result
print(n_gram(sentence_string,2))
print(n_gram(sentence_list,2))
['I-a', 'a-m', 'm-a', 'a-n', 'n-N', 'N-L', 'L-P', 'P-e', 'e-r']
['I-am', 'am-an', 'an-NLPer']
- 内包表記版(shiracamus様より)
import re
sentence_string = "I am an NLPer"
sentence_list = sentence_string.split()
def n_gram(sequence, n):
u"""文字列で渡されたとき文字bi-gram,リストとして渡されたとき単語bi-gramとして扱われる。
"""
if isinstance(sequence, str):
sequence = list(re.sub("[,. ]", "", sequence))
return ['-'.join(sequence[i:i+n])
for i in range(len(sequence) - n + 1)]
print(n_gram(sentence_string, 2))
print(n_gram(sentence_list, 2))
内包表記、使えますね。。
06. 集合
"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
import re
X = "paraparaparadise"
Y = "paragraph"
def n_gram(sequence,n):
result = []
if isinstance(sequence,str):
sequence = list(re.sub("[,. ]","",sequence))
for i in range(len(sequence)- n+1):
result.append('-'.join(sequence[i:i+n]))
return result
X = (set(n_gram(X,2)))
Y = (set(n_gram(Y,2)))
print("X:",X)
print("Y:",Y)
print("和集合:",X | Y)
print("積集合:",X & Y)
print("差集合1:",X - Y)
print("差集合2:",Y - X)
if 's-e' in X:
print('seはXに含まれる')
if 's-e' in Y:
print('seはYに含まれる')
X: {'a-d', 'a-r', 'r-a', 'i-s', 's-e', 'd-i', 'p-a', 'a-p'}
Y: {'a-r', 'r-a', 'p-h', 'g-r', 'a-g', 'p-a', 'a-p'}
和集合: {'a-r', 'i-s', 'p-a', 'a-p', 'a-d', 'r-a', 'p-h', 'g-r', 's-e', 'd-i', 'a-g'}
積集合: {'p-a', 'a-p', 'a-r', 'r-a'}
差集合1: {'a-d', 'd-i', 's-e', 'i-s'}
差集合2: {'a-g', 'g-r', 'p-h'}
seはXに含まれる
07.テンプレートによる文生成
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.
def make_sentence(x,y,z):
print(u"{0}時の{1}は{2}".format(x,y,z))
make_sentence(x = 12,y = "気温",z = 22.4)
12時の気温は22.4
08.暗号文
与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
- 英小文字ならば(219 - 文字コード)の文字に置換
- その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.
import re
pat = re.compile(u"[a-z]")
def cipher(string):
return ''.join(chr(219-ord(c)) if pat.match(c) else c for c in string)
if __name__ == "__main__":
sentence = u"Hello world!"
ciphertext = cipher(sentence)
print(sentence)
print(ciphertext)
print(cipher(ciphertext))
re.compile('[a-z]')
Hello world!
Hvool dliow!
Hello world!
- 正規表現不使用版(shiracamus様より)
def cipher(string):
return ''.join(chr(219 - ord(c)) if c.islower() else c for c in string)
if __name__ == "__main__":
sentence = u"Hello world!"
ciphertext = cipher(sentence)
print(sentence)
print(ciphertext)
print(cipher(ciphertext))
str.islower()が使えるんですね。str.islower()は小文字+大文字小文字の区別の無い文字の文字列の場合でもTrue
になるみたいです。
chr(i)
Unicode コードポイントが整数 i である文字を表す文字列を返します。例えば chr(97) は文字列 'a' を、 chr(8364) は文字列 '€' を返します。 ord() の逆です。
引数の有効な範囲は 0 から 1,114,111 (16 進数で 0x10FFFF) です。 i が範囲外の場合 ValueError が送出されます。
09. Typoglycemia
スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually
understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.
from random import shuffle
def change_order(sentence):
produced_word_list = []
word_list = sentence.split(' ')
for word in word_list:
if len(word) <= 4:
produced_word_list.append(word)
else:
middle = list(word[1:-1])
shuffle(middle)
produced_word = word[0] + ''.join(middle) + word[-1]
produced_word_list.append(produced_word)
return ' '.join(produced_word_list)
sentence = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(change_order(sentence))
I cnud'olt bvieele that I cluod aucltaly uestradnnd what I was reading : the pnemonaehl power of the huamn mind .
- 別解
import random
def change_order(sentence):
produced_word_list = []
word_list = sentence.split(' ')
for word in word_list:
if len(word) <= 4:
produced_word_list.append(word)
else:
middle_list = list(word[1:-1])
new_middle = ''
while len(middle_list) > 0:
rnd = random.randint(0,len(middle_list)-1)
new_middle += middle_list.pop(rnd)
new_word = word[0] + new_middle + word[-1]
produced_word_list.append(new_word)
return ' '.join(produced_word_list)
sentence = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(change_order(sentence))
I cl'oundt beevile that I culod aauctlly unnetdarsd what I was rdeaing : the pneoenhaml peowr of the haumn mind .
- ジェネレータ と random.shuffle 版(shiracamus様より)
import random
def change_order(sentence):
def produced_words():
word_list = sentence.split()
for word in word_list:
if len(word) <= 4:
yield word
else:
middle = list(word[1:-1])
random.shuffle(middle)
yield word[0] + ''.join(middle) + word[-1]
return ' '.join(produced_words())
sentence = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(change_order(sentence))
###ジェネレーターの謎
ジェネレーター、、、苦手意識しかなかったですが、せっかくなので、
クラスとイテレータ - Dive Into Python 3 日本語版
Pythonのiterator
を参考に理解しようとした。。
ジェネレーターは
for文の中にnext()
とiter()
メソッドが含まれていて、ジェネレーター関数中の__iter__
,__next__
メソッドを呼び出している(?)という認識でよいのでしょうか。。
iteratorオブジェクトは2つのメソッドを持っていて、
__iter__
メソッドは自分自身を返し、__next__
メソッドは次の要素を返す。
クラスとイテレータ - Dive Into Python 3 日本語版よりメモ
>>> import plural6
>>> r1 = plural6.LazyRules()
>>> r2 = plural6.LazyRules()
>>> r1.rules_filename ①
'plural6-rules.txt'
>>> r2.rules_filename
'plural6-rules.txt'
>>> r2.rules_filename = 'r2-override.txt' ②
>>> r2.rules_filename
'r2-override.txt'
>>> r1.rules_filename
'plural6-rules.txt'
>>> r2.__class__.rules_filename ③
'plural6-rules.txt'
>>> r2.__class__.rules_filename = 'papayawhip.txt' ④
>>> r1.rules_filename
'papayawhip.txt'
>>> r2.rules_filename ⑤
'r2-overridetxt'
① このクラスの各々のインスタンスは、クラスで定義された値を持つ属性rules_filenameを受け継いでいる。
② 1つのインスタンスの属性値を変更しても、他のインスタンスの属性値には影響を与えず……
③ ……クラス属性も変更しない。クラス自体にアクセスするための特殊属性__class__を使うことで、(個々のインスタンスの属性ではなく)クラス属性を参照できる。
④ クラス属性を変更すると、まだ値を受け継いでいるインスタンス(ここではr1)はその影響を受ける。
⑤ その属性を上書きしているインスタンス(ここではr2)は影響を受けない。
個々のインスタンスとクラスのインスタンスは別物。
class MyIterator(object):
def __init__(self, *numbers):
self._numbers = numbers
self._i = 0
def __iter__(self):
# next()はselfが実装してるのでそのままselfを返す
return self
def next(self):
if self._i == len(self._numbers):
raise StopIteration()
value = self._numbers[self._i]
self._i += 1
return value
my_iterator = MyIterator(10, 20, 30)
for num in my_iterator:
print 'hello %d' % num
class Fib:
'''iterator that yields numbers in the Fibonacci sequence'''
def __init__(self, max):
self.max = max
def __iter__(self):
self.a = 0
self.b = 1
return self
def __next__(self):
fib = self.a
if fib > self.max:
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib
Classの中でnext
が特殊メソッド__next__
の場合と普通のメソッドnext
の場合とあるのは、なぜなのか。
Class内の__next__
は外部のメソッドnext
から呼び出しているのに対し、Class内のnext
はClassの中で実行まで行っている(?)
わけわからない。