09 : 言語処理100本ノックでPythonのお勉強

第一章 : 09 Typoglycemia

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

Typoglycemiaとは、ニコニコ大百科に以下の用に書かれている。

Typoglycemiaとは、単語を構成する文字を並べ替えても、最初と最後の文字が合っていれば読めてしまう現象のことである。
[例え]
こんちには みさなん おんげき ですか? わしたは げんき です。 この ぶんょしう は いりぎす の ケブンッリジ だがいく の けゅきんう の けっか にんんげ は もじ を にしんき する とき その さしいょ と さいご の もさじえ あいてっれば じばんゅん は めくちちゃゃ でも ちんゃと よめる という けゅきんう に もづいとて わざと もじの じんばゅん を いかれえて あまりす。 どでうす? ちんゃと よゃちめう でしょ? ちんゃと よためら はのんう よしろく

文字が入れ替わっていてもなんか読めちゃいますね。これは欧米人が英語を読んでも同様のようです。 今回のプログラムはTypoglycemiaに変換する関数を書きます。 プログラムはこのような感じになりました。

import random

def typoglycemia(sentence):
    result_list = []
    sentence_list = sentence.split(' ')
    for i in range(len(sentence_list)):
        if len(sentence_list[i]) > 4:
            word = sentence_list[i]
            first = word[0]
            last = word[-1]
            middle = word[1:-1]
            rand_middle = random.sample(middle, len(middle))
            rand_middle = ''.join(rand_middle)
            word = first + rand_middle + last
            result_list.append(word)
        else:
            result_list.append(sentence_list[i])
    result_sentence = ' '.join(result_list)
    return result_sentence

l = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(typoglycemia(l))

上の書き方はいろいろ微妙なので、ネットで調べた内容を参考に以下のように書き換えました。

import random

def typoglycemia(sentence):
    '''
    param : 対象の文字列
    return : 変換した文字列
    '''
    result = []
    for word in sentence.split(' '):
        if len(word) <= 3:
            result.append(word)
        else:
            w_list = list(word[1:-1])
            random.shuffle(w_list)
            result.append(word[0] + ''.join(w_list) + word[-1])

    return ' '.join(result)

target = input('文字列を入力してください。\n')

result = typoglycemia(target)
print('変換結果\n' + result)

こちらを参考にしました。というかほぼ同じです。ありがとうございました。

素人の言語処理100本ノック:09 - Qiita

08 : 言語処理100本ノックでPythonのお勉強

第一章 : 08 暗号文

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

プログラムはこんな感じになりました。

# ord()関数はアスキーコードを取得します。
# chr()関数はアスキーコードを文字に変換します。

def cipher(t):
    result = ''
    for c in t:
        if c.islower():
            result += chr(219 - ord(c))
        else:
            result += c
    return result

t = "helloworld"

# 暗号化
coded = cipher(t)
print('暗号化:' + coded)

# 復号化
decoded = cipher(coded)
print('復号化:' + decoded)
   

07 : 言語処理100本ノックでPythonのお勉強

第一章 : 07 テンプレートによる文作成

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

pythonではテンプレートによる文を作成する場合、format()関数を使うと簡単に書ける。

プログラムなこんな感じになります。

def f(x, y, z):
    result = "{0}時の{1}は{2}".format(x ,y ,z)
    return result

print(f(x=12,y="気温",z=22.4))

06 : 言語処理100本ノックでPythonのお勉強

第一章 : 06 集合

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

集合はPythonだとset()関数を使うことで作成することができます。

プログラムは以下のようになります。n_gram()関数は05で作成したプログラムを使います。

l = 'paraparaparadise'
l2 = 'paragraph'

X = set(n_gram(l, 2))
Y = set(n_gram(l2, 2))

# 和集合
OR = X | Y
print(OR)

# 積集合
AND = X & Y
print(AND)

# 差集合
SUB = X - Y
print(SUB)

# 'se'が含まれているか
result = 'se' in X
print(result)
result = 'se' in Y
print(result)

05 : 言語処理100本ノックでPythonのお勉強

第一章 : 05のお題 n-gram

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

そもそもn-gramとは何ぞや。bi-gramとは何ぞや。wikipediaを調べると以下のようなものらしい。

「N文字インデックス法」「Nグラム法」などともいう。検索対象を単語単位ではなく文字単位で分解し、後続の N-1 文字を含めた状態で出現頻度を求める方法。Nの値が1なら「ユニグラム(英: uni-gram)」、2なら「バイグラム(英: bi-gram)」、3なら「トライグラム(英: tri-gram)」と呼ばれる。

例えば、「こんにちは」という文字列をbi-gramで分割すると、「こん」、「んに」、「にち」、「ちは」という文字列に分けられる。tri-gramで分割すると、「こんに」、「んにち」、「にちは」という文字列にわけられる。n-gramは検索などに使われる技術のよう。

プログラムは以下のようになります。

def n_gram(target, n):
    result = []
    for i in range(0, len(target) -n + 1):
        result.append(target[i:i+n])
    return result

l = "I am an NLPer"
words = l.split(" ")

# bi-gram
result = n_gram(words, 2)
print(result)

# tri-gram
result = n_gram(words, 3)
print(result)

04 : 言語処理100本ノックでPythonのお勉強

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

プログラムはこんな感じです。

first_w_list = [1, 5, 6, 7, 8, 9, 15, 16, 19]
target = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
results = {}

words = target.split(" ")

for (num, word) in enumerate(words, 1):
    if num in first_w_list:
        results[word[0:1]] = num
    else:
        results[word[0:2]] = num

print(results)

最初に先頭の1文字だけ取り出す単語の番号のリスト、文全体の文字列、結果を入れる辞書を作成します。

次に文全体を文字列から単語のリストに変換します。

そして、enumerate()関数を使って単語のリストを回し、numがfirst_w_listに入ってる場合は、単語の1文字目をスライスして辞書のキーに入れ、辞書の値にその番号を入れます。入っていない場合は単語の2文字目までをスライスし、辞書のキーに入れ、辞書の値にその番号を入れます。

enumerate()関数を使うと、リストの値だけでなく、インデックス番号も取得することができます。for(num, word) in enumerate(words, 1)と書くと、wordsリストに入っている要素のインデックスをnumに返し、値をwordに返すことができます。enumerate()関数の第2引数(今回の場合は1)は最初のインデックス番号をいくつにするか指定するために使います。

03 : 言語処理100本ノックでPythonのお勉強

第一章 : 03のお題 円周率

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

l = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
words = l.split(" ")
result = []

for word in words:
    count = 0
    for char in word:
        if char.isalpha():
        count += 1
    result.append(count)

print(result)

まず、split関数で文を単語のリストに分ける。 次に結果を入れるための空のリストを用意する そして、単語のリストをfor文で回し、その中で単語の中にあるアルファベットをis.alpha()関数で調べ、アルファベットであれば、 countを1増やす。最後に結果のリストにアルファベットの数の要素をappend()関数で追加する。