morikomorou’s blog

自分が学んだことなどの備忘録的なやつ

【python】言語処理100本ノック2020を解く(第1章)


はじめに

面白そうな勉強の題材を見つけましたので解いていきたいと思います。

全10章あるので1章から順番に、いけるところまでやってみたいと思います。
難しかったら途中で逃避するかもしれませんが許してください。。。

1章は割と簡単だったのでまとめて全部やります。




第1章:準備運動

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

00. 文字列の逆順

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

逆順に出力するだけなのでstr[::-1]でOKですね

txt = 'stressed'
print(txt[::-1])

# output:
# desserts

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

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

1個飛ばしで出力すればいいのでstr[::2]でOKですね

txt = 'パタトクカシーー'
print(txt[::2])

# output:
# パトカー

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

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

パトカーとタクシーどちらも同じ文字数なのでzip()関数でそれぞれ抜き出したあと足し合わせます

txt1 = 'パトカー'
txt2 = 'タクシー'
txt = [a + b for a, b in zip(txt1, txt2)]
print(''.join(txt))

# output:
# パタトクカシーー

03. 円周率

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

単語に分解するにはスペースで分割すればいいですが、その前に不要なカンマやピリオドを除外しておく必要があります。

txt = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
txt = txt.replace(',', '') # , を除去
txt = txt.replace('.', '') # . を除去
words = txt.split() # 単語に分解
words_len = [len(x) for x in words]
print(words_len)

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

これも先ほどと大して変わらないですね。
MnじゃなくてMiなのが少し気になりますが。。。

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

# output:
# {'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ってのが初耳でした。
文字n-gramは、文字列の先頭からn文字ずつ順番に文字を抜き出すことで、単語n-gramは同様にn単語ずつ順番に単語を抜き出すことだそうです。

def n_gram(txt, n):
    res = []
    if len(txt) < n:
        return res
    for i in range(len(txt) - n + 1):
        res.append(txt[i:i+n])
    return res


txt = "I am an NLPer"
print('文字bi-gram: ', n_gram(txt, 2))
print('単語bi-gram: ', n_gram(txt.split(), 2))

# output:
# 文字bi-gram:  ['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
# 単語bi-gram:  [['I', 'am'], ['am', 'an'], ['an', 'NLPer']]

06. 集合

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

先ほどの関数を使えば簡単です。

txt1 = "paraparaparadise"
txt2 = "paragraph"
X = set(n_gram(txt1, 2))
Y = set(n_gram(txt2, 2))
print('X: ', X)
print('Y: ', Y)
print('和集合: ', X | Y)
print('積集合: ', X & Y)
print('差集合: ', X - Y)
print("'se'がXに含まれるか?: ", 'se' in X)
print("'se'がYに含まれるか?: ", 'se' in Y)

# output:
# X:  {'se', 'di', 'ad', 'pa', 'ra', 'is', 'ap', 'ar'}
# Y:  {'gr', 'pa', 'ph', 'ra', 'ag', 'ap', 'ar'}
# 和集合:  {'se', 'di', 'ad', 'gr', 'pa', 'ph', 'ra', 'is', 'ag', 'ap', 'ar'}
# 積集合:  {'pa', 'ap', 'ra', 'ar'}
# 差集合:  {'ad', 'se', 'is', 'di'}
# 'se'がXに含まれるか?:  True
# 'se'がYに含まれるか?:  False

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

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

競プロでの入力受け取りと一緒ですね

x, y, z = input('x y z?').split()
print("{}時の{}は{}".format(x, y, z))

08. 暗号文

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

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力

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

Unicode変換に用いるchr関数やord関数は存在を知らなかった…
文字コードはord()関数で確認できます。
小文字の文字コードはaが97、bが98、…、zが122なので、219-文字コードという操作は、アルファベットを逆順に変換する操作といえます。
つまりaはzに(219-97=122)、bはyに(219-98=121)、cはxに(219-99=120)といった具合です。
なので、同じ関数で暗号化、複号化が可能です

def cipher(txt):
    ans = ""
    for x in txt:
        if x.islower():
            ans += chr(219 - ord(x))
        else:
            ans += x
    return ans

txt = "I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print('暗号化: ', cipher(txt))
print('復号化: ', cipher(cipher(txt)))

# output:
# 暗号化:  I xlfowm’g yvorvev gszg I xlfow zxgfzoob fmwvihgzmw dszg I dzh ivzwrmt : gsv ksvmlnvmzo kldvi lu gsv sfnzm nrmw .
# 復号化:  I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind .

09. Typoglycemia

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

random.sampleモジュールを使用してランダムに並び替えを実装します。

import random

def shuffle(txt):
    ans = []
    words = txt.split()
    for word in words:
        if len(word) <= 4:
            ans.append(word)
        else:
            new_word = word[0] \
              + ''.join(random.sample(word[1:-1], len(word) - 2)) + word[-1]
            ans.append(new_word)
    return ' '.join(ans)

txt = "I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
shuffle(txt)

# output:
# 'I clduon’t bevelie that I colud aulatlcy uadrnntesd what I was rdneaig : the pehoanneml pwoer of the hmaun mind .'

おわりに

とりあえず1章完です。
なんか頭の体操みたいで楽しいですね。
この調子でどんどん解いていきます。