前回まででモジュールや変数、条件分岐、繰り返し処理といったPythonの機能を一通り紹介しました。これらの機能を使うことで高度なプログラムが書けるようになりましたが、プログラムの中身も徐々に複雑になってきました。

そこで今度は「関数」という、プログラムの処理をまとめる仕組みを紹介します。関数でプログラムを処理ごとにまとめることで、読みやすいプログラムを作成できるようになるのです。

今回と次回は、第11~13回で作成したQA対話システムの主要な処理部分を関数を使って書き直すことで、関数の使い方を説明していきたいと思います。

作業ディレクトリは「C:\Users\user\Documents\pychat\function」としますので、事前に作成しておいてください。

※ 今回の内容は、第12回で紹介した関数re.searchの使い方に自信がない方は、先に復習しておきましょう!

似た処理を含むプログラム

第13回では、文字列が正規表現にマッチするかどうかを確認する関数re.searchを使ってQA対話システムを作成しました。QA対話システムの主要な処理は、ユーザー発話の質問(文字列)が質問回答リスト中の質問パターン(正規表現)のいずれかにマッチするかどうかを確認することです。

そこでまず初めに、文字列が正規表現のリストのいずれかにマッチするかどうかを確認するプログラム「match.py」を書いてみましょう。

# match.py - 文字列がパターンにマッチするか確認するプログラム
import re

patterns = ["富士山.*高さ", "東京.*区.*いくつ"]

text_fuji = "ねぇ、富士山の高さは?"
matched_fuji = False
for pattern in patterns:
    if re.search(pattern, text_fuji):
        matched_fuji = True
        break
print(matched_fuji)

このプログラムでは、4行目の正規表現のリストpatternsのいずれかの要素に6行目の「text_fuji」に束縛された文字列「ねぇ、富士山の高さは?」がマッチするかどうかを判定します。

7行目の「matched_fuji」はマッチしたかどうかを記録するブール値(True/False)を値に持つ変数です。「True」の場合マッチしたことを、「False」の場合マッチしなかったことを表します。

そして8行目のfor文でリストpatterns中の要素(パターン)を1つずつ変数patternに束縛します。9行目では関数re.searchを使って変数patternが「text_fuji」にマッチするかを確認し、マッチした場合は10行目で変数matched_fujiに「True」を入れて、breakによりfor文を抜けます。

連載が進むに連れて、こうした処理を書く機会も増えてきたので、だんだんと慣れてきたのではないでしょうか。

さて、プログラムの概略を把握したので実際に実行してみましょう。「text_fuji」の文字列「ねぇ、富士山の高さは?」は正規表現リスト中のパターン「”富士山.*高さ”」にマッチするので「True」と表示されるはずです。試してみましょう。

$ python match.py
True

続いて、「東京の区っていくつあるの?」という文字列もマッチするかどうかを確認してみましょう。先ほどのプログラムにコードを追加し、「match_multi.py」というファイルを作成します。

# match_multi.py - 文字列がパターンにマッチするか確認するプログラム
import re

patterns = ["富士山.*高さ", "東京.*区.*いくつ"]

text_fuji = "ねぇ、富士山の高さは?"
matched_fuji = False
for pattern in patterns:
    if re.search(pattern, text_fuji):
        matched_fuji = True
        break
print(matched_fuji)

# New: 「東京の区っていくつあるの?」をチェックするプログラムを追加
text_tokyo = "東京の区っていくつあるの?"
matched_tokyo = False
for pattern in patterns:
    if re.search(pattern, text_tokyo):
        matched_tokyo = True
        break
print(matched_tokyo)

「東京の区っていくつあるの?」という文がマッチするかどうかを確認するために「New:」で始まるコメント以下のコードを新しく追加しました。text_fujiとmatched_fujiの代わりに、text_tokyoとmatched_tokyoという変数を導入した以外は、前のプログラムとほぼ同じ処理を追加しただけであることがわかるかと思います。

文字列「東京の区っていくつあるの?」は正規表現のパターン「”東京.区.いくつ”」にマッチするので、実行すると2つ目の「True」も表示されます。

$ python match_multi.py
True
True

期待通りに動作はしているのですが、ここで改めてmatch_multi.pyをご覧になってみてください。皆さんはどう感じるでしょうか?

「似たような処理を何度も書かないといけなくて面倒だなあ……」
「変数名が違うだけの似た処理が増えて、ミスが発生しやすそうだなあ……」
「片方の処理を変更したら、もう片方も同じように変更しないと! 忘れそうだなあ……」

どれも、当然の感想です。ミスが起きないか心配になるのもわかります。

プログラム内で何度も登場する「似たような処理」は、本質的には同じ処理であるはずです。そうした処理を関数でまとめて再利用することで、プログラミングの手間が軽減され、プログラム自体もわかりやすくなります。

今回は、あえて似た処理を含むプログラムを書いてみることで関数が必要となる状況について説明しました。次回は、このプログラムに関数を導入し、わかりやすく書き直してみたいと思います。

著者紹介


株式会社NTTドコモ
R&Dイノベーション本部 サービスイノベーション部
阿部憲幸

2015年京都大学大学院理学研究科数学・数理解析専攻修了。 同年、NECに入社。 2016年から国立研究開発法人情報通信研究機構出向。 2018年より現職。 自然言語処理、特に対話システムの研究開発に従事。 毎日話したくなるAIを夢見て日夜コーディングに励む。
GitHub:https://github.com/noriyukipy