この記事は、Python Advent Calendar 2022 25日目の記事です。
Python Advent Calendar 2022は現在4つに冗長化しており、24日目は
- saosao885さんによる「DataFrame型?Datatable型と違うの?と指定範囲のデータ取得を練習します。」
- u1and0さんによる「OSS開発wikiツールGrowiのサイドバーに記事ランキングを作った話」
- hkwsdgea_ttt2さんによる「【物体検出2022】BoT-SORTを使ってYOLOv7のモデルで物体追跡(MOT)を実装する」
- cotton-gluonさんによる「開発中のモジュールの紹介?」の予定
でした。
記事の積ん読が加速している!
はじめに
フォッフォッフォッ、みんなメリクリ!🎄🍗🎅
今回はよいPython使いのみんなにnikkieサンタからプレゼントがあるぞ。
プレゼントというのは、Pythonの具象構文の世界が垣間見えるチケットじゃ。
フォッフォッフォッ、頭の上に「?」が浮かんでいるようじゃな。
安心せい、具象構文と聞いてピンとこなくても大丈夫じゃ。
まずはif文の書き方をどう説明するかを考えてみようかの3。
目次
- はじめに
- 目次
- プログラミング入門者にif文をどう説明しますか?
- Pythonのドキュメントではどう説明しているでしょうか?
- 卒業BNF。Python 3.9からはPEG
- PEGで表すif文の書き方
- この先の旅路へ:参考文献リスト
- 終わりに
プログラミング入門者にif
文をどう説明しますか?
プログラミングの経験がない方がPythonでプログラミングに入門する状況を想定します。
よいPython使いのあなたはif
文について説明することになりました4。
どう説明しますか?
このエントリは、この説明が私には非常に難しかったという経験に基づきます。
よろしければちょっと考えてから読み進めてみてください。
ちなみにnikkieがなんとか編み出した説明はこちらにあります:
7. 3大構造 二. 分岐 — start-programming-with-python ドキュメント
もし他のプログラミング言語で入門済みだったら
この場合はif
文の説明はぐっと易しくなると思います。
なぜならその方が知っている言語のif
文について質問し、「Pythonだとこう書くんですよ」というやり方で説明できるからです。
他のプログラミング言語で入門しているということは、
if
文が担う分岐という制御構造5(概念)- その言語での
if
文の書き方
を知っており、後者(書き方)について「Pythonではこうなっている」と説明するだけでよいわけです。
ですが、いま考えているのはプログラミングの経験がない方へのif
文の説明です。
「if
文の書き方」だけでなく「分岐という制御構造」についても伝える必要がありますね。
if
文の書き方の説明:elif
やelse
の有無で場合分け?
このうち書き方の説明が難しいように思われます。
まず「分岐という制御構造」は、以下のような説明になるのではないでしょうか6。
分岐とは、「条件 に従って 処理を いくつかに 分け 、そのうちの どれか1つを実行 する」ことです。
制御構造を伝えた後は「if
文の書き方」を伝えます。
Pythonのif
文は(A)else
の有無、(B)elif
が1つ/複数/なし7とバリエーションが考えられますよね。
私には全バリエーション列挙する説明しか浮かびませんでした(他の言語で入門済みだとバリエーションは一通り知っているはずなので、説明はもっと手短になると思います)。
if ...
if
のみ。(A)else
なし。(B)elif
なし
if ... else ...
- (A)
else
あり。(B)elif
なし
- (A)
if ... elif ...
- (A)
else
なし。(B)elif
1つ
- (A)
if ... elif ... elif ...
- (A)
else
なし。(B)elif
複数
- (A)
if ... elif ... else ...
- (A)
else
あり。(B)elif
1つ
- (A)
if ... elif ... elif ... else ...
- (A)
else
あり。(B)elif
複数
- (A)
世に出ている無数のPythonによるプログラミング入門書(※他の言語による入門書も含めてもよいです)では、著者がベストを尽くした説明がされているはずです8。
この場合分けは私も当時の最善を尽くしました。
※その中のどれが最強かというのはここで扱いたいことではありません(一般に最強はなく、個々人にとってベスト=最推しな解説があるという立場です)。
さて、場合分けをして全バリエーション説明した後、私の頭の中には1つの疑問が浮かびました。
Pythonのドキュメントではどう説明しているでしょうか?
それが「if
文の書き方はドキュメントでどう説明されているんだろう」という疑問です。
なお、入門者の方にドキュメントを見てもらうという意図はありません。
「これだけ頭を悩ませてif
文の書き方を説明したわけだけど、これはnikkieの理解が複雑になりすぎている可能性があるわけで、自分の理解を更新するためにドキュメントを見てみるか(場合分け以外の説明のヒントが得られるかも)」というモチベーションでした。
文について(Python言語リファレンス)
Pythonのプログラムの構成要素は文(statement)9です(文を式などさらに分解することもできますが、ここでは文について見ていきます)。
文には「単純文」と「複合文」の2種類があり、Python言語リファレンスの下にドキュメンテーションされています。
単純文とは、単一の論理行内に収められる文です。(7. 単純文 より)
複合文には、他の文 (のグループ) が入ります; 複合文は、中に入っている他の文の実行の制御に何らかのやり方で影響を及ぼします。(8. 複合文 より)
if
文は複合文で、8.1.で書き方が説明されています。
それがこちら
if_stmt ::= "if" assignment_expression ":" suite ("elif" assignment_expression ":" suite)* ["else" ":" suite]
拡張BNF
このわずか3行が「if
文の書き方」を示しています!
私がひねり出した場合分けは6通りもありましたが、ドキュメントではわずか3行ですよ、すごくないですか!
これは「BNF 文法記法に手を加えた」記法です(1.2.)。
記法の読み方は1.2.で解説されています。
ざっくりと読んでみると
"if" assignment_expression ":" suite
assignment_expression
は別途定義されていますが、話を単純にするために10、Python 3.8で登場した代入式(:=
)も含む式と考えてくださいsuite
はコードのブロックです- 実はインデントってその文(たち)がsuiteであることを示しているんですよ!
この部分が表しているのは
- キーワード
if
で始まる - 式(
assignment_expression
)が来る - コロン(
:
)が来る - コードのブロック(suite)が来る
という書き方です。
このように書かれたif
からsuiteの部分は、どんなif
文にもありますよね(必須ということです)。
("elif" assignment_expression ":" suite)*
*
は0回以上の繰り返しを表します。
なので、()
でまとめた"elif" assignment_expression ":" suite
が0回以上ということを表しています。
これでelif
の部分が0回/1回/複数回、どれも表せていますよね!
["else" ":" suite]
[]
はオプション(0回または1回繰り返し)です。
else
の部分が0回または1回ということを表しているわけです。
この3行でif
文の書き方の場合分けは網羅できているわけです。
拡張BNFという記法が読める必要はありますが、それでもたった3行で書き方が説明されているというのは衝撃でした。
拡張BNFによるif
文の書き方は、if文
という文法事項の定義です。
拡張BNFにマッチするように書いたif
文は処理系に処理されますし、マッチしなければ処理系はSyntaxError
を送出します。
※念のため繰り返しますが、入門者の方に拡張BNFを読むことを薦める意図は一切ありません。
言語リファレンスまで見に行くと、if
文の書き方はBNFを使って驚くほど簡潔に定義されているわけです。
卒業BNF。Python 3.9からはPEG
こうしてBNFを読み、if
文の書き方の簡潔な定義を味わったわけですが、このお話はもうちょっと続きます。
python.jp の以下の記事はご存知でしょうか?
「Python言語のパーザが置き換えられ」は以下のPEPにリンクしています。
複合文のドキュメントは(2022年12月現在も)BNFなのですが、Pythonは3.9からはPEGなるもので文法を定義しているのです!
PEGで表すif
文の書き方
PEGはParsing Expression Grammar(解析表現文法)の略です。
PEGと拡張BNFの違いは実はわずかです。
それはBNFの選言(alternation 「または」)の代わりに、PEGは順序付き選択を採用しています。
これだけの違いでPEGはBNFの課題を解決しているのですが(エレガント!)、詳しくは『Pythonで学ぶ解析表現文法と構文解析』を参照ください(3.1にあります)。
さて、PEGで表されたif
文の書き方は、10. 完全な文法仕様にあります11。
if_stmt: | 'if' named_expression ':' block elif_stmt | 'if' named_expression ':' block [else_block] elif_stmt: | 'elif' named_expression ':' block elif_stmt | 'elif' named_expression ':' block [else_block] else_block: | 'else' ':' block
PEGの読み方は https://peps.python.org/pep-0617/#syntax にあります。
[]
はオプション(0回または1回の繰り返し)
named_expression
は、(話を単純にするために)BNFと同様、代入式(:=
)も含む式と思いましょう。
block
の定義を見に行くと
block: | NEWLINE INDENT statements DEDENT | simple_stmts
となっており、これは
- 改行しインデントされた複数の文(単純文でも複合文でもよい)
- 改行せず続く複数の単純文
です(PEGではインデントはblockの1パターンを表すわけですね)。
if_stmt
の定義は2つの選択肢を示しています。
'if' named_expression ':' block elif_stmt
elif
があるケース
'if' named_expression ':' block [else_block]
elif
がないケース
elif_stmt
の定義は再帰的ですよね(1つ目の選択肢)。
拡張BNFと比べたら定義に使う行数は増えましたが、elif
の有無にだけ着目し、(私がひねり出した6パターンの場合分けより)スッキリとした印象です。
この先の旅路へ:参考文献リスト
コンピュータ・サイエンス
学生時代には全然関心を持てなかったのですが、プログラミング言語が書けるようになってくるとなかなか興味深いです。
構文解析
2022年に出た本で「PEGに入門できる!」と私は即積みました。
BNFと何が違うのかなど、日本語で入門できるのは大変ありがたいです。
Guidoさんが著したPEGについてのシリーズもあります(PEP 617の脚注[4]に見つけました)。
エラーメッセージ改善の裏にPEGあり!
takanoryさんによる今月の「Python Monthly Topics」もPEGの話題があります。
ここで紹介されているPabloさんのトークは私も聞きました(多分takanoryさんほど理解できてないです)。
このトークを聞いて私が驚いたのは、「if
文でヘッダのコロン忘れてない?」というようなエラーメッセージには、PEGが一役買っているということです!
Python 3.9でPEGを導入して可能になった発展を後続のバージョンで実施しているんですよ!
# https://github.com/python/cpython/blob/3.11/Grammar/python.gram#L1288-L1291 invalid_if_stmt: | 'if' named_expression NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") } # 省略
この理解にはPEGのActionという概念へのキャッチアップが必要と思われます(Guidoさんのシリーズで示されてもいます)。
PyCon JP 2022より
PyCon JP 2022は(観測範囲では)具象構文をメインで扱う話はなかったように思われますが、抽象構文(AST: Abstract Syntax Tree)について扱った(と思われる)トークがいくつかありました。
他の言語に目を向ける
Pythonで具象構文を学べるリソースは少ないかもしれませんが、他の言語のリソースを活用できるかもしれません。
2022年11月のLint Nightは、この可能性を見出せ、非常によかったです。
nikkieのアウトプットより
興味を持って、2022年アウトプットしました。
同じ興味を持った方に、少しでも参考になれば幸いです。
2022/01 BNF(PyCon Kyushu 2022 Kumamoto)
#pycon9ku このあと15:40〜サブ会場 #pycon9kuB で話します。
— nikkie にっきー 🎤10/1 XP祭り 10/14-15 PyCon JP (@ftnext) 2022年1月22日
「文に立ち返ってPython再入門」https://t.co/OvLYWTb4mx
話すこと
・Pythonのコロンとインデントの意味→ドキュメントを追っての理解を伝えます
・すごいんだよ、BNF記法での構文表現!感動の共有に挑戦します
2022/09 PEG(PyCon APAC) 英語発表
Revisit Python from statements and PEG
※スライドはここからどうぞ
終わりに
「if
文の書き方をどう説明するか」から始めて具象構文の世界のごくごく一部を紹介しました。
拡張BNFやPEGでif
文の文法は簡潔に表現されています!(一緒に読んでみましたね)
そして、具象構文やその先のトピックとして参考文献リストも共有しました。
ひょんなことから「具象構文(Pythonの見た目)って面白い」と私は興味を持ったわけですが、これはPythonを使っているなら絶対知っていなければならない領域ではないと思います。
ただ全く役に立たないわけでもなく、私自身は具象構文を理解したことで、コーディング中にVS Codeが指摘してくる赤線の意味はなんとなく分かり、知る前より手応えを持ってPythonを書いている感覚はあります。
そして、どのプログラミング言語にも具象構文はあるわけで、「Python以外のプログラミング言語でもなんとかなるんじゃないかな」と思えるようにもなりました。
具象構文やそれより下の世界、これはもしかすると「教養」という言葉が当てはまる領域なのかもしれません。
明日の仕事に即役立つわけではないけれど、例えば古典を学ぶといった姿勢と具象文法には通じるものを感じます。
この旅路は同じ道を歩む人の姿が見えず、孤独との闘いでもあるように感じます。
このエントリをきっかけに具象構文やその先の領域に興味を持つ方がいらっしゃったら素晴らしいことだと思います(面白いからみんなやろうぜ!)。
2023年は興味ある人のPython構文研究会みたいなことができたらいいなと思っています13。
冗長化されたPython Advent Calendar 2022には空白の日付もあります。
抽象構文に限らずPythonに関して何かアウトプットできるネタをお持ちの方はこの年末年始に時を戻そう参加してみてはいかがでしょうか(みんなで目指そう、トータル100記事!)
- PyCon JP 2022の「Pythonとアスタリスク 🐍🌟💫🐍🌟💫」と同じ挨拶で登場です。実は私の中ではLiella!の嵐さん(a.k.a ちぃちゃん🐙)を召喚しています↩
- 先日業務委託契約を締結しました✌️(注:たまたま25日目が空いていたのでなにか書くかと思い立ったのです)↩
- フォッフォッフォッ、サンタ口調はここでおしまいとします↩
- 「私はよいPython使いではないです」という方も、入門者の方を見かけたらできる範囲でいいので助けてみてください。回答するときには: Discordサーバ - python.jpがとてもよいです。 例「読んでいて「いらっ」としてしまうような質問が投稿されるときもあります。そういうときには、いちいち注文をつけるのではなく、無視するようにしてください。」↩
-
アルゴリズムの基本構造は、順次・分岐・反復の3つです。
if
文はその中の分岐を実現します。こちらのスライド http://www.mi.u-tokyo.ac.jp/consortium2/pdf/4-23_literacy_level_note.pdf が分かりやすいと思います(スライド15、スライド21)↩ - もっといい説明が浮かんだ/見かけたことがある方は@ftnextまでぜひ教えてください↩
-
(B)は細かすぎる場合分けかもしれませんが、複数回使うことができるのは
elif
だけなので、何も前提知識がない方のつまづきがなるべくないよう、0回使う(=使わない)/1回使う/複数回(=2回以上)使うで場合分けすることにしました↩ - オススメの説明があったら教えてください↩
- https://docs.python.org/ja/3/glossary.html#term-statement↩
- これを追うのでも結構紙面を使うので省略しました。興味ある方は追ってみてください。私は楽しかったです!↩
- GitHubだと https://github.com/python/cpython/blob/3.11/Grammar/python.gram です(tagを変えてみてください)↩
- 具象構文(抽象構文)でオススメの文献があったらぜひ教えてください!↩
- はっ!誰も来なくても独りでやればいいんだし♪↩