2. Pythonインタプリタとスクリプトの体験、計算的思考

2.1. テキストエディタ ATOM のインストール

  • 公式サイトATOMからmacOS版をダウンロード。

  • atom-mac.zip として保存されるので、これを展開する。

  • 展開された Atom.app を「Applications」フォルダに移動(ドラッグ&ドロップ)。

  • 起動しやすくするために Dock に登録。


2.2. 用語集

2.2.1. shell, prompt, command, statement, code, program, script, source

用語集1

ターミナル上でアプリケーション(シェルやPython等)とやり取りを行う際、アプリ側が入力を受け付けられる状態か否かを明示するために用いられるのが プロンプト(prompt) である。ターミナルを起動した直後の状態からプロンプトが出力される。具体的な命令を入力せずにEnterキーだけを入力すると、キーが入力される度にプロンプトが出力される。この「プロンプトが返ってくる」ことを通して、こちらの入力を受け付けているということが分かる。

ターミナルを起動した直後に出力されるプロンプトを、上記では「シェルのプロンプト」と書いている。これは、ターミナルを起動すると自動的に シェル(shell) が起動され、実際に応答しているのはシェルだからだ。シェル上でpythonと命令を入力すると「Pythonインタプリタ(python interpretor)」が起動し、プロンプトの出力形式が変わる。「>>> 」の状態ならPythonインタプリタが入力を受け付けている状態である。このようにプロンプトから「今何とやり取りしているのか」を意識しながら取り組もう。

なお後述するが、シェルやPythonインタプリタに対する入力は「標準入力(standard input, stdinと省略)」、逆に得られる出力を「標準出力(standard output, stdout)」と呼ぶ。「標準」と付けている理由は、入出力をする経路が複数あるからである。例えば、ターミナルウィンドウを複数立ち上げているとしよう。それぞれのウィンドウでPythonインタプリタを起動している場合、それぞれのインタプリタはどのように入力を受け、またどのように出力したら良いだろうか。このような混乱をしなくて済むように、特に何も指定しなかった場合にはインタプリタを立ち上げたウィンドウを対象とするように紐付けられている。標準で用意されている入出力先のことを標準入力、標準出力と呼ぶ。

Pythonインタプリタが処理を実行する際の単位を コマンドや命令(command, statement) と呼び、それらを集合を コードやプログラム、またソース(code, program, source) と呼ぶ。また、コードやプログラムをファイルとして保存したものを スクリプト(スクリプトファイル; script file)、ソース(ソースファイル; source file) と呼ぶ。文脈に応じて意味が異なることもあるが、他人とのやり取り中における「ソース見せて」という文は、「書いたコードを見せて」「保存したコードを見せて」と同等の意味で使われる。


2.2.2. operator, type, int, float, str, bool, variable, comparison

用語集2

基本的な型、演算、type()関数で型を調べられることを覚えておこう。

==!=比較演算子(comparison operator) と呼ばれており、演算子の左右が等しいか否かを確認するために用いられる。例えば「先着100人までプレゼントする」ようなコードを書こうとすると、応募のあったユーザを順番に並べて一人ずつカウントする。そのカウントした値を変数countに保存しておき、「count == 100」の比較結果が真であれば、100人分処理し終えたということを判断できる。比較演算子の結果は真偽のみであり、 True もしくは False というbool型(boolearn, ブーリアンとも呼ばれる) のリテラルで返される。

Note

bool型リテラルの TrueFalse は、文字列ではない点に注意しよう。例えば、

>>> True == 'True'

において、左辺はboolean型のTrue(真)というリテラルであり、右辺はstr型のリテラルである。異なるリテラルであるため、比較結果は False となる。

print()やtyp()のように、名称(引数) の形式で実行する命令を 関数(functions) と呼ぶ。数学における関数の場合、例えば y = 2x + 1 という1次関数では、変数xの値を具体的に決めると出力yを算出することができる。この変数xに相当する部分、すなわち入力を指定する部分をプログラムでは 引数(arguments, parameters) と呼ぶ。

プログラムにおける関数は、引数で与えられる情報を元に、何かしらの処理を行う。具体的には、print()関数では「引数として与えられたリテラル、もしくは変数の中身を標準出力に書き出す」し、type()関数では「引数として与えられたリテラル、もしくは変数の中身の型を調べ、返す」という処理を実行する。関数は自身でも作ることが可能であり、後日取り扱う。興味のある人は教科書で予習してみよう。


2.2.3. comment, boolean

用語集3

命令文に # が含まれていると、そこから行末までは コメント(comment) として扱われる。コメントは人間がメモをしておくために用いる。また、一時的にコードを実行したくないが削除したくない場合に、コードの冒頭に#をつけることで該当行をコメントとして解釈させることもあり、このような行為は コメントアウト(comment out) と呼ぶ。

>, >=, <, <= は数値を用いた比較演算子である。「より大きい」「以上」のようにその値を含むのかどうかを区別できるようになろう。この使い分けを間違うと「100人に対しプレゼントする」つもりでも99人までしか処理できなかった、ということになる。よくあるミスであり、このようなミスを発見しやすくするための手法を境界テストと呼ぶ(後日やります)。

and, or, not論理演算子(boolean operator) と呼ばれており、左右のリテラルがbool型であることを前提として処理する。例えば「条件1も条件2も揃っているのでステージクリアと見做す」ような場合には and 演算子による確認が向いている。or, not についてもどういう用途がありそうか、考えてみよう。

Note

#を含めるとその後ろがコメント扱いになることは既に述べたとおりである。では「#を含めた文字列」をstr型として用意したり、そのままprint関数で出力したりするにはどうしたら良いのだろうか。具体的には次のようなコードを書くと、どうなるだろうか。

>>> statement = '朝早くて眠い #今授業中'
>>> print(statement)

答えは 朝早くて眠い #今授業中 が出力される。すなわち、#を含む文字列がそのまま後ろまで出力されており、コメント扱いになっていない。これは 優先順位 の問題である。例えば数学における 1 + 3 * 2 の結果は8ではなく7である。これは足し算より先に掛け算が優先されるからだ。これと同様に、プログラムの記述には優先順位が定められている。今回のケースでは # よりも ' による文字列定義が優先されているため、#がそのまま文字列として扱われていることになる。

同様に print(')') は、紛らわしいが ) という1文字を文字列として扱い、それをprint関数が出力する。このように命令文の処理は予め指定された優先順位に従って処理されるということを覚えておこう。

Note

上記のコード例ではまず変数 statement を宣言して値を保存し、次にその変数の中身を出力している。このコードを書く際には1文字ずつ全てを手打ちする必要はなく、Tabキーによる入力補完 を利用すると良い。Tabキーとは Controlキーの上にある右矢印が印字されているキーのことだ。

入力補完をやってみよう。まず1行目はそもそもまだコンピュータが見知らぬ変数なので、そのまま手打ち入力する必要がある。2行目は print(s まで入力した状態でTabキーを1〜2回押してみよう。1回目は通知音のようなものがなるだけだが、2回目は次のように出力されるはずだ。

>>> statement = '朝早くて眠い #今授業中'
>>> print(s
set(           slice(         statement      str(           super(
setattr(       sorted(        staticmethod(  sum(           
>>> print(s

上記は「sから始まる変数もしくは予めPythonで用意されている関数」の一覧が表示された結果である。続けて print(state まで入力した状態でTabキーを押してみよう。そうすると自動的に print(statement まで入力した状態になるはずだ。このように、頭文字1文字以上を入力した状態でTabキーを押すと、そこから推測できる文字列の一覧を示したり、もしくは一意に定まる場合には最後まで入力してくれる機能のことを入力の自動補完と呼ぶ。全ての変数名や関数名を一文字も間違えないようにこわごわ入力するのではなく、補完機能を使えばスムーズに入力できることを覚えておこう。

Tip

上記で述べたように処理には順序が定められている。興味のある人は公式ドキュメントの評価順序、優先順位を眺めてみよう。


2.3. 文字列結合とstr.format形式

>>> enemy = 'スライム'
>>>
>>> # case 1: 出力したい文字列を1変数に用意してからprint()する。
>>> output = enemy + 'が2体現れた'
>>> print(output)
スライムが2体現れた
>>>
>>> # case 2: print()内で演算処理する。
>>> print(enemy + 'が2体現れた')
スライムが2体現れた
>>>
>>> # case 3: str.format()形式を利用する。
>>> output = '{}が2体現れた'.format(enemy)
>>> print(output)
スライムが2体現れた
>>> print('{}が2体現れた'.format(enemy))
スライムが2体現れた

ここでは文字列結合の例を3つ示している。

1つ目は、文字列を保存した変数に対して文字列リテラルを結合している例である。

2つ目は、引数内に演算子が含まれるケースを示している。このように演算子が含まれる場合、演算子がなくなるまで演算を実行し、その結果を引数として利用する。

3つ目は、とても見づらいがPython特有の文字列作成方法である。いくつか新しい要素が出てきているため、分解して考えてみよう。

  • (1) '{}が2体現れた' は、str型の文字列を表している。 試しに '{}が2体現れた' のみを引数として指定した場合には、そのまま出力されるはずである。

  • (2) (1)の後ろに続く .format(引数) は、直前の文字列における {} を引数に置き換えた文字列を生成する。 例えば、

>>> '{}'.format(1)

は、{}を数字の1に置き換えた文字列を生成する。

>>> '{}'.format(enemy)

は、変数enemyが何か値を保存しているならば、その中身を{}の部分に置き換え、文字列を生成する。

>>> '{}{}体現れた'.format(enemy,2)

は、1つ目の{}を変数enemyの中身に置き換え、2つ目の{}を数字の2に置き換えた文字列を生成する。

このような一見複雑に見える文字列結合の手段を用意しているのは、単に +演算子による結合を用いた場合にわかりづらくなるケースがあるからだ。例えば、

>>> '1+1' + 'は' + '2' + 'です'

というコードを人間が見たとき、どの部分がリテラルなのかは直感的には分かりづらい。これに対し、

>>> '{}+{}{}です'.format(1,1,2)

であれば、3つのリテラルがあること、そのリテラルを組み合わせた文字列を生成したいことがわかりやすい。 このように「人間にとっての読みやすさ(リーダブルコード, readable code)」の観点から、str.format()形式 が提供されている。


2.4. スクリプトの利用

2.4.1. スクリプトとは?

ここまでは、Pythonインタプリタを起動し、そこでコードを直接書くことで命令をコンピュータに伝え、実行させていた。しかしこの方法では、同じ命令をさせたい場合にはその都度同じコードを書く必要があり、手間である。この手間を省くため、コードをテキストファイルに保存して実行する方法がある。この「コードが保存されたファイル」のことを スクリプトやソースコード(script, script file, source code,,,) と呼んでいる。

スクリプトとしてコードを保存する際には、テキストエディタ(text editor) を使おう。テキストエディタとは、テキストのみを編集(edit)してファイル保存するためのソフトである。プログラミングにおいては「プログラミング言語仕様に則ったコード」をそのままテキストとして保存する必要があるため、テキストに特化したソフトを使う。テキストエディタには色んな種類があり、それぞれ癖があるので、いろいろ試してみることで使いやすいものを後日探してみるといいだろう。


2.4.2. スクリプトを書いて動かしてみよう

今回は Atom をインストールして、使ってみよう。Downloadからソフトウェアをダウンロードすると、atom-mac.zip~/Downloads/ 以下に保存される。

Note

~/チルダ、スラッシュ と発音し、実行したユーザ自身のホームディレクトリ を示すために用いる表記である。このフォルダやディレクトリとはOS毎に呼び名が変わるだけで、基本的には同一のものと考えておこう。

あなたが使っているPCは、初めてセットアップした際にあなた専用のアカウントを作ったはずだ。これは複数のアカウントを作ることが出来る(例えば家族で共用して使うとか)。その際に各アカウント毎に「ここは私専用のフォルダ」「あそこはあなた専用のフォルダ」といったことを決めておいた方が使いやすい。各自のフォルダをホームディレクトリと呼ぶ。

例えば當間のアカウントは tnal で作成しており、PC上のホームディレクトリは /Users/tnal/ である。この / はルートディレクトリと呼ばれており、ルートディレクトリから対象ディレクトリまでのパスを全て羅列する書き方を フルパスや絶対パス(full path, absolute path) と呼ぶ。一方で ~/~/Downloads/ のように、ルートディレクトリ以外を基準とした書き方を 相対パス(relative path) と呼ぶ。

Finderを起動すると自動的にホームディレクトリを表示してくれる。この中にある「ダウンロード」という日本語フォルダ名は、実際には「Downloads」という名前であり、OSの言語指定で日本語を指定している場合に自動で翻訳されているだけである。同様に留学生が自身の母国語を指定している場合には、Finder上では見かけ上指定した言語で表示される。

Finderからダウンロードフォルダを開くと、先程の atom-mac.zip が見つかるはずだ。これをダブルクリックすると Atom.app というアプリケーションが展開されるので、これを「アプリケーション」フォルダにドラッグ・アンド・ドロップで移動(=アプリケーションとしてコピー)しよう。これ以降はアプリケーションフォルダにあるAtom.appをダブルクリックすると起動することができる。Dockにも登録しておくと便利だ。

Atomを起動した直後はWelcome, Welcome Guide, untitledの3つのタブが開いていると思われる。細かいドキュメント等は一先ず置いておき、ここではコードを保存して実行するまでの流れを覚えよう。

  • まず(1)untitledタブをクリックする。

  • (2)そこにPythonコードを書き並べる。

  • (3)書き終えたら、今回は「test.py」という名前で保存しよう。実際にはファイル名は変数名として利用できるものなら何でも構わないが、拡張子は必ず「.py」とすること。

    • ファイルを保存するディレクトリは、自分が覚えられる場所ならどこでも構わない。プログラミング1用のディレクトリを作成し、そこに保存するようにすると他授業のファイルと区別しやすいが、各自がやりやすい形で保存して構わない。例えばホームディレクトリにtest.pyという名前で保存したのならば、そのファイルの場所は ~/test.py となる。ホームディレクトリの下に prog1 というディレクトリを作成し、その中に week2.py という名前で保存したのならば、~/prog/week2.py として保存されている。

  • ファイルを保存し終えたら、(4)ターミナルを起動し、保存したディレクトリに移動しよう。ホームディレクトリに保存したのであれば、ターミナルを起動した直後にその場所にいる。

    • ファイルの有無を確認するためには、シェル上で ls コマンド(一覧listの省略)を入力し、実行してみよう。想定通りならばファイルが見つかるはずだ。ホームディレクトリと異なるディレクトリ、例えば前述のように ~/prog1 というディレクトリに保存したのであれば、cd コマンド(change directoryの省略)を使って移動しよう。具体的には、シェル上で cd prog1 と実行するとそこに移動できる。移動したあとは同様に ls コマンドでファイルを確認しよう。

  • ファイルが見つかったならば、(5) python ファイル名 として実行してみよう。ちゃんとファイルがあり、中に書かれているコードが適切ならば、想定通りの動作結果が出力されるはずだ。ls, cd 等はUNIXコマンドとも呼ばれており、詳細はプログラミング演習1で扱う。

これで、ファイルとして保存したプログラムを実行できるようになった。何度でも再実行することができるし、一部分を修正して利用する(=再利用する)こともしやすくなったはずだ。


2.4.3. スクリプトファイル vs. インタプリタ

スクリプトファイル vs. インタプリタ

慣れてくると最初からテキストエディタを使った開発が早いが、言語仕様に慣れていない段階ではいろいろと細かく試しながら確認できるインタプリタの方が便利なことも多い。例えばAtomとターミナルの2つを立ち上げておき、インタプリタで納得できるコードが書けたらそれをエディタにコピーして保存するとか、双方の利点を使い分けるといいだろう。


2.5. 変数名・ファイル名の命名規則(教科書にない内容)

命名規則

変数名やファイル名として使うことの出来る文字と、規約を覚えておこう。冒頭3項目はPythonの言語仕様としてのルールであり、厳守する必要がある。

その次の Level3〜Level4、Google Python Style Guide は規約の例、言い換えるとローカルルールである。言語仕様を守っただけの変数名では分かりにくい名称が使われることも多く、読みにくいコードになることがある。例えば四角形の面積を求めるために、

>>> area = height * width

と、

>>> a = b * c

とではどちらがコードの意図を汲み取りやすいだろうか。1つ目は意図の汲み取りやすいコードとなっており、その分バグを発見しやすくなるため保守しやすい。現場ではコードを一度書いて終わりではなく、何度も修正・機能追加等を伴う編集をしながら使い続けていくため、読みやすさを意識したコードが望ましい。そのためにも組織毎にローカルルールを定め、それに則る名称を利用することが一般的である。多くの場合に共通するLevel2ぐらいまでを意識して名付けるようにしてみよう。


2.6. マニュアルの参照(教科書にない内容)

  • Python公式ドキュメント

  • help()関数 on Pythonインタプリタ

    • 例: >>> help(print)

  • Google先生

    • e.g., 「python print」 or 「python3 print」

    • できるだけ複数単語で検索し、絞り込む。単に「print」だと、別のプログラミング言語のページがヒットしたり、「配布資料(プリント)」のことがヒットする可能性。単一単語では判別困難。

  • 注意

    • Python 2 <-> Python 3で異なることがある。

    • Web上の情報は間違ってることがある。

関数に関するドキュメントは、公式ドキュメントから探すか、直接Pythonインタプリタ上でhelp()関数を使うと参照することができる。例えばprint()関数について調べると以下のような出力が得られるはずだ。

>>> help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
  (中略)
(END)
  • 上記の print(value, ..., sep=' ', 略) は使い方を簡易的に示している。ENDでマニュアルの最後であり、マニュアル画面から抜け出してインタプリタに戻りたい場合には、q(quitの頭文字) を入力しよう。

  • value はリテラルや変数を記述する欄であり、それらをカンマ(,)で区切って列挙できることを「value, …,」として示している。

  • その次の sep=' ' は、数行下に説明があり「string inserted between values, default a space.」とある。これは、value欄に複数の値を列挙した場合、それらの値を何らかの文字を挿入することをしめしている。例えば、

>>> print(1,2,3)

の結果を確認してみよう。自動的にスペースが挿入された状態で指定した値が出力されるはずだ。このように「特に指定しなかった場合に自動で使われる値」のことを デフォルト値もしくは単にデフォルト(default) と呼ぶ。今回の場合にはスペースがデフォルト値として指定されているため、スペースが挿入されて出力されたことになる。パラメータsepを変更するには以下のように指定しよう。

>>> print(1,2,3,sep='#')

Tip

関数に与える引数は順序に注意しよう。print(1,2,sep='#',3) のように書いても正しく処理できない。この点は関数について学ぶ時に改めて振り返ることにする。

end は、出力後に自動で追加される制御文字である。'\n' はこれで1文字の制御文字であり、「改行文字」を意味する。改行文字をsepとして設定するとどうなるだろうか。試してみよう。

Tip

Flush は通常無視して構わない。補足しておくとこれはコードの実行速度も考慮した話であり、四則演算と比べると「標準出力に書き出す」という処理はとても遅い。このため、print文が大量にあると全体として処理が遅くなってしまうが、通常はなるべくその負荷が小さくなるような工夫がなされている。逆にこの工夫のために、出力し終える前に別のコードの影響でプログラム全体が異常終了してしまうことがある。このような状況を避けるため、必ず出力し終えてから次に進むように指定するのがflushである。


2.7. 計算的思考(教科書1章の補足)

計算的思考

左の宣言的知識で述べている平方根の定義は、定義として正しい。しかしこの定義からは平方根を求めることはできない。

右の命令的知識で述べているレシピ・手順・手続きは、平方根を求めるための手続きとして書き下されており、近似的な平方根を求めることが可能。このように 十分に定義された入力が与えられ、有限個の命令群で用意された手続きにより出力を求めることができる手続き のことを アルゴリズム(algorithm) と呼ぶ。より詳細は2年次の必修講義「アルゴリズムとデータ」にて学ぶ。ここでは「入力と出力を明確にし、それをどのように処理するのかという手続きを考える必要があること」ぐらいを心に留めておこう。


2.8. 翻訳に至る3ステップの例

1週目にプログラミングには3つのステップ(理解・整理・翻訳)があることを述べた。ここでは具体例を上げてその流れを説明する。

例として、「授業を担当している先生が、受講生に対して質問をするためにランダムに一人を選びたい」と考えているとしよう。すなわち Step 1の「実現したいこと」は「受講生の中から1名をランダムに選ぶ」 である。

さて、これを実現するために必要な手順 はどうなるだろうか。例えば次のように考えるかもしれない。この時点では誤っていても良く、取り敢えず可能性がありそうな手順として整理してみることに集中しよう。

  • (1) 受講生一覧を用意。

  • (2) 一覧をシャッフルする。

  • (3) シャッフルされた一覧から、先頭の一人を抜き出す。

ここで もし「受講生一覧の用意」の翻訳の仕方が分からない場合には、それをさらに細分化 して考えていこう。例えば次のようになるだろう。

  • (1-1) 受講生は学生記番号で区別されている。だから学籍番号で準備することを考えよう。

  • (1-2) 2021年度の知能情報コースの学籍番号は必ず 2157 から始まり、下二桁が入学者数と一致している。すなわち 215701〜2157xx の数字を用意することができれば、「受講生一覧の用意」をしたことになる。

最後にこれをプログラミング言語で翻訳することになる。例えばこうなるだろう。

students = list(range(215701, 215760))
import random
random.shuffule(students)
students.pop()

上記はまだ教えていない部分があるが、流れとしてはこのように、理解したことを手順として整理し、プログラミング言語で記述できるレベルまで細分化する。最後にコードに落とし込む という手順を取る。

Note

プログラミングの過程において、もし手順に誤りがあったとわかればその時点で修正したら良いし、翻訳時点で誤りがあった場合にも同様にその時点で修正したら良い。思いつく方法を試し、修正しながらゴールに辿り着く力こそが重要である。だからこそ本授業ではいわゆるテストはせず、時間にゆとりを持つ形で取り組めるよう課題で評価する。

Tip

プログラミングは道具であり、コンピュータさえあればどこでも何でも(メモリ不足等でなければ)実現することができる。失敗を恐れず試行錯誤する術を学ぼう。


2.9. 振り返り

まとめ


2.10. 復習・予習

  • 復習

    • 適宜過去資料及び教科書を参照しよう。

  • 予習

    • 教科書読み

      • 2.2 Branching Programs

      • 2.3 Strings and Input

    • 余裕があれば

      • 2.4 Iteration

      • (3章スキップ)

      • 4.1〜4.1.1 Function Definitions