nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonのif文の書き方ってどう説明します? 言語リファレンスの具象構文の世界の紹介、そしてその先へのいざない

この記事は、Python Advent Calendar 2022 25日目の記事です。
Python Advent Calendar 2022は現在4つに冗長化しており、24日目は

でした。
記事の積ん読が加速している!

はじめに

ういっすー✌️1 nikkieサンタです2

フォッフォッフォッ、みんなメリクリ!🎄🍗🎅
今回はよいPython使いのみんなにnikkieサンタからプレゼントがあるぞ。
プレゼントというのは、Python具象構文の世界が垣間見えるチケットじゃ。
フォッフォッフォッ、頭の上に「?」が浮かんでいるようじゃな。
安心せい、具象構文と聞いてピンとこなくても大丈夫じゃ。
まずはif文の書き方をどう説明するかを考えてみようかの3

目次

プログラミング入門者にif文をどう説明しますか?

プログラミングの経験がない方がPythonでプログラミングに入門する状況を想定します。
よいPython使いのあなたはif文について説明することになりました4
どう説明しますか?

このエントリは、この説明が私には非常に難しかったという経験に基づきます。
よろしければちょっと考えてから読み進めてみてください。

ちなみにnikkieがなんとか編み出した説明はこちらにあります:
7. 3大構造 二. 分岐 — start-programming-with-python ドキュメント

もし他のプログラミング言語で入門済みだったら

この場合はif文の説明はぐっと易しくなると思います。
なぜならその方が知っている言語のif文について質問し、「Pythonだとこう書くんですよ」というやり方で説明できるからです。
他のプログラミング言語で入門しているということは、

  • if文が担う分岐という制御構造5(概念)
  • その言語でのif文の書き方

を知っており、後者(書き方)について「Pythonではこうなっている」と説明するだけでよいわけです。

ですが、いま考えているのはプログラミングの経験がない方へのif文の説明です。
if文の書き方」だけでなく「分岐という制御構造」についても伝える必要がありますね。

if文の書き方の説明:elifelseの有無で場合分け?

このうち書き方の説明が難しいように思われます。

まず「分岐という制御構造」は、以下のような説明になるのではないでしょうか6

分岐とは、「条件 に従って 処理を いくつかに 分け 、そのうちの どれか1つを実行 する」ことです。

制御構造を伝えた後は「if文の書き方」を伝えます。
Pythonif文は(A)elseの有無、(B)elifが1つ/複数/なし7とバリエーションが考えられますよね。

私には全バリエーション列挙する説明しか浮かびませんでした(他の言語で入門済みだとバリエーションは一通り知っているはずなので、説明はもっと手短になると思います)。

  1. if ...
    • ifのみ。(A)elseなし。(B)elifなし
  2. if ... else ...
    • (A)elseあり。(B)elifなし
  3. if ... elif ...
    • (A)elseなし。(B)elif1つ
  4. if ... elif ... elif ...
    • (A)elseなし。(B)elif複数
  5. if ... elif ... else ...
    • (A)elseあり。(B)elif1つ
  6. if ... elif ... elif ... else ...
    • (A)elseあり。(B)elif複数

世に出ている無数の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は別途定義されていますが、話を単純にするために10Python 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を使って驚くほど簡潔に定義されているわけです。

卒業BNFPython 3.9からはPEG

こうしてBNFを読み、if文の書き方の簡潔な定義を味わったわけですが、このお話はもうちょっと続きます。

python.jp の以下の記事はご存知でしょうか?

しかし、Python 3.9 ではPython言語のパーザが置き換えられ、こういった記述が可能となりました。

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パターンの場合分けより)スッキリとした印象です。

この先の旅路へ:参考文献リスト

(私の積ん読リストでもあります12

コンピュータ・サイエンス

学生時代には全然関心を持てなかったのですが、プログラミング言語が書けるようになってくるとなかなか興味深いです。

構文解析

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

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記事!)


  1. PyCon JP 2022の「Pythonとアスタリスク 🐍🌟💫🐍🌟💫」と同じ挨拶で登場です。実は私の中ではLiella!の嵐さん(a.k.a ちぃちゃん🐙)を召喚しています
  2. 先日業務委託契約を締結しました✌️(注:たまたま25日目が空いていたのでなにか書くかと思い立ったのです)
  3. フォッフォッフォッ、サンタ口調はここでおしまいとします
  4. 「私はよいPython使いではないです」という方も、入門者の方を見かけたらできる範囲でいいので助けてみてください。回答するときには: Discordサーバ - python.jpがとてもよいです。 例「読んでいて「いらっ」としてしまうような質問が投稿されるときもあります。そういうときには、いちいち注文をつけるのではなく、無視するようにしてください。
  5. アルゴリズムの基本構造は、順次・分岐・反復の3つです。if文はその中の分岐を実現します。こちらのスライド http://www.mi.u-tokyo.ac.jp/consortium2/pdf/4-23_literacy_level_note.pdf が分かりやすいと思います(スライド15、スライド21)
  6. もっといい説明が浮かんだ/見かけたことがある方は@ftnextまでぜひ教えてください
  7. (B)は細かすぎる場合分けかもしれませんが、複数回使うことができるのはelifだけなので、何も前提知識がない方のつまづきがなるべくないよう、0回使う(=使わない)/1回使う/複数回(=2回以上)使うで場合分けすることにしました
  8. オススメの説明があったら教えてください
  9. https://docs.python.org/ja/3/glossary.html#term-statement
  10. これを追うのでも結構紙面を使うので省略しました。興味ある方は追ってみてください。私は楽しかったです!
  11. GitHubだと https://github.com/python/cpython/blob/3.11/Grammar/python.gram です(tagを変えてみてください)
  12. 具象構文(抽象構文)でオススメの文献があったらぜひ教えてください!
  13. はっ!誰も来なくても独りでやればいいんだし♪