プログラムを書いてみる


Shuhei Iitsuka

エディタの使い方

エディタとは、プログラムのコード(=スクリプト)を入力するためのツールです。
プログラミングをする上でずっと付き合うことになる重要なものです。

CUI (詳しくは前項) で利用するエディタの代表的なものには vim と emacs があります。
この2つのエディタは人気を二分しており、しばしば宗教戦争が起きます。
今回は emacs を使って簡単なプログラムを紹介します。(ちなみに僕は vim 派です)
プログラミング言語は Python 2.7 を使います。

emacs でスクリプトの編集を行うには、まずシェルで

$ emacs test.py

と入力します。”py”は、Python スクリプトのファイル拡張子です。これは「これから emacs で test.py というファイルを編集するよ」という意味です。
GUIのソフトで新しくファイルを作る場合は保存するときに初めて名前をつけることが多いですが、CUI でプログラムを書くときは編集する前に名前をつけます。


2016/04/25 追記:
GUIを使っている人は、同じ名前のファイルを手動で作って、そのまま編集すればOKです。

ここから実際にプログラムコードを入力していきます。
まず以下の行を入力してください。

print "Hello World"

これは「”Hello World”という文字列を出力する」というスクリプトです。
入力したらコントロールキーを押しながら”x”,”s”の順に入力します。
これで入力した内容を”test.py”に保存します。
次に、コントロールキーを押しながら”x”,”c”の順に入力します。
これでemacsを終了します。

以下は、emacsの基本的な操作コマンドです。使っていく中で覚えていきましょう。
C- は「Ctrlキーを押しながら」という意味です。

  • C-x C-f ファイルを開く
  • C-x C-s ファイルを保存
  • C-x C-c ファイルを保存しemacsを終了
  • C-a カーソルを行頭へ移動
  • C-e カーソルを行末へ移動
  • C-d カーソル上の文字を削除
  • C-k カーソルから行末まで削除

さらに便利な UNIX コマンドを知りたい方はEmacs入門 – ドットインストールが参考になります。
Vim はこちら→Vim入門 – ドットインストール

プログラムを入力したファイルを作成したらlsして現在地にファイルがあるか確認します。

$ ls
test.py

次に、pythonで test.py を実行します。

$ python test.py


2016/04/25 追記:
プログラミング環境によっては、”実行”や”Run”、”Execute”といったボタンがあるので、それを押しましょう。

すると”Hello World”という文字列が出力されます。
実はこれでプログラムが作成できました!とても簡単ですが”Hello World”という文字を出力するプログラムができたのです!!

・・・と言われても、まだプログラムを書いた実感がわかないと思うので、もう少しプログラムっぽいことをやっていきましょう。

3の倍数と3のつく数のときだけアホになるプログラムを書く

世界のナベアツというお笑い芸人が流行らせたネタに「3の倍数と3が付く数字のときだけアホになります」というものがあります。これをプログラムで書くとどうなるのでしょうか?

考え方の整理

まずプログラムを書き始める前に、この「3の倍数と3が付く数字のときだけアホになる」という現象について正確に考えてみましょう。
もし、あなたが「1 から 100 までこれやってみて」と言われたら、どのように頭を動かしますか?
たとえば、こういうふうに考えることができます。

  1. よし、まずは 1 から 100 まで数えよう。
  2. そして声に出そうとする数字が 3 で割り切れる時は、アホになろう。
  3. あ、数字に 3 がつく時もアホにならなきゃいけないな。

実は、このように整理できた時点でプログラムはほとんどできています。
あとは、この頭の動かし方をプログラミング言語で記すことこそが、プログラムを書くということなのです。

基本的な構文

ここでポイントになるのは、上の手順で太字にした部分です。
「AからBまで」というのは、繰り返しや範囲を指定する言葉です。一方「Aの時」というのは、条件を指定する言葉です。
プログラミング言語ではなく日本語でしたが、この二つでおおまかな頭の動かし方の手順を記述することができました。
実は、プログラムもこの繰り返し条件分岐のパーツから成り立っているのです。

繰り返し

多くのプログラミング言語では for 文によって繰り返しを表現します。
Python でもこの構文を用います。
たとえば、1から100まで何かをするときは、

for i in range(1, 101):

というように書きます。これは変数(数字や文字列などのデータを格納するの名前だと思ってください)iが 1 から 100 までになるまでの間、ということを表しています。
range()のカッコの中に入れる数字(引数と呼びます)の2つ目は繰り返しに含まれないことに注意してください。

条件分岐

同様に、条件分岐は多くのプログラミング言語で if 文によって表現します。
Python では

if i == 3:

というように書きます。これは変数iが3に等しい時、ということを表しています。

実際のコード

では、for と if を使ってプログラムを書いてみましょう。「3の倍数と3が付く数字のときだけアホになる」スクリプトは以下のようになります。

#coding: utf8
for i in range(1, 101):
    if i%3 == 0 or "3" in str(i):
        print "アホ"
    else:
        print i

ところどころ行頭が右にずれているのがわかると思いますが、このように行をずらすことをインデントと呼びます。
Python ではインデントが構文上の意味を持っており、インデントが変わるとプログラムの挙動も変わってしまいますので注意してください。
通常はタブやスペース2個分または4個分によってずらします。プログラム内で統一されていればどの方法を用いても大丈夫です。

1行目の#coding: utf8はおまじないだと思ってください。ただ、Pythonでコードを書く際にはかならず先頭行にこの一行を記入する癖をつけてください。

2行目のfor繰り返しを意味する文法です。ここでは「i が 1 から 100 になるまで動作を繰り返す」という意味になります。
この繰り返し命令は、この下のインデントが保持されている間ずっと有効です。

3行目のif条件分岐を意味する文法です。ここでは「もし i が 3 で割り切れたら、または i に “3” が含まれたら」という意味になります。
この条件に当てはまれば、4行目に移って「アホ」と表示します。
当てはまらなければ、6行目に移って単純に数字を表示します。鋭い方は気づいたかもしれませんが、5行目のelseは「それ以外の時」を表しており、ifとセットで使われます。

ここで str() という文字が気になったかもしれません。
このように括弧付きで表されるものを関数と呼びます。
関数は()の中に入った変数に操作を加えて吐き出します。
str()関数は()に入った変数をなんでもかんでも文字列に変換して吐き出す関数です。
たとえば、str(56)"56"を吐き出します。

ここで 56 と “56” は何が違うの?と思ったかもしれません。プログラミングの世界では、この2つは明白に違います。
“56”は文字で、56は数値なのです。

“56”は文字なので、”5″と”6″という文字がくっついたものです。文字なので、足し算や引き算はできません。
たとえば、Python では"56"+"1"561です。
一方、56 は数値なので、足し算や引き算ができます。56+157です。
ちょっと混乱するかもしれませんが、まずは文字列と数値が異なることだけ覚えておいてください。

以上のプログラムを実行してみると、下記のような文字が現れるはずです。ただしくアホになっていますか?

1
2
アホ
4
5
アホ
7
...

ちょっとふざけた問題でしたが、実はこれはFizz Buzz問題と言われる、れっきとした問題の亜種で、プログラマの腕試しに使われている問題です。
コードのロジックを追いながらぜひ咀嚼してください。

観客の反応を集計する

ここまで「アホになる」お笑いネタを題材にプログラムを紹介してきました。
ところで、お笑いには観客アンケートがつきものです。ここからはアンケートを集計するプログラムを書いていきましょう。

たとえばお笑いライブの観客にアンケートをとった結果、以下のようなデータが集まったとします。

名前 性別 評価点 (100点満点)
田中花子 女性 58
鈴木一郎 男性 76
山田太郎 男性 69
佐藤恵子 女性 62
石井あや 女性 71

評価点を聞くのはシビアすぎるので実際のアンケートには無いと思いますが、ここでは満足度のようなモノを表すと考えてください。
このデータからお客さんの男女比や評価点の平均を出したりして情報を引き出すには、まずこのデータをプログラムで扱うことができる形にする必要があります。

そこで役に立つのが、データ構造です。多くのプログラミング言語には、こういった構造化されたデータを扱うための仕組みが存在しています。
まずは、データ構造で表したいデータをよく眺めます。すると、次のことがわかります。

  • このデータはアンケートの連なりでできている。
  • ひとつひとつのアンケートは、「名前」や「性別」といった項目名と、そのでできている。

Pythonの場合は、このデータの連なりを表すのにリスト[]項目名と値の関係を表すのにディクショナリ{}を使います。

リスト

リストは、データの連なりを格納するためのデータ構造です。たとえば、

a = [1,1,2,3,5,8]

というように、値の列を格納することができます。
a[0]とすると、リストの 0 番目の値(この場合は1)にアクセスすることができます。
sum(a)とすると、リストの合計(この場合は20)が返ってきます。
len(a)とすると、リストの長さ(この場合は6)が返ってきます。
実際にprintして確かめてみましょう。

リストに値を加えたい場合は、

a.append(13)

とすると、リストaの末尾に値が追加されます。他にも色々と操作方法があるので、必要に応じて調べましょう。

ディクショナリ

ディクショナリは項目名と値のペアを格納するためのデータ構造です。たとえば、

a = {"name": "Alice", "gender": "female"}

というように、複数の項目名と値のペアを格納することができます。
a の性別を知りたい場合は、a["gender"]とすると、aの項目 gender の値(この場合は “female”)が返ってきます。

新たに項目名と値のペアを追加したい場合は、

a["age"] = 7

とします。この場合はaの新たな項目に age が、その値として 7 が追加されます。

また、リストと組み合わせて使うことも可能です。

members = [
    {"name": "Alice", "gender": "female"},
    {"name": "Bob", "gender": "male"}
]

データ構造をつかってアンケートデータを表す

それでは、さっそくアンケートデータをデータ構造を使って表してみましょう。
アンケートひとつひとつは「名前」や「性別」といった項目名と値であり、アンケートデータ全体はその連なりなので、ディクショナリのリストで表すのがいいでしょう。

data = [
    {"name": "田中花子", "gender": "女性", "score": 58},
    {"name": "鈴木一郎", "gender": "男性", "score": 76},
    {"name": "山田太郎", "gender": "男性", "score": 69},
    {"name": "佐藤恵子", "gender": "女性", "score": 62},
    {"name": "石井あや", "gender": "女性", "score": 71}
]

このデータをもとに、簡単な処理を行なってみましょう。

男女それぞれの観客数を出す

male = 0
female = 0
for datum in data:
    if datum["gender"] == "男性":
        male = male + 1
    if datum["gender"] == "女性":
        female = female + 1
print male, female

たとえば、観客の男性と女性それぞれの数を出したい場合には、まず

male = 0

というように男性の数を格納する変数を用意するといいでしょう。

Python では for 文でリストひとつひとつの要素を順番に取り出すことができます。

for datum in data:

とすることで、data に含まれる要素ひとつひとつを datum という名前で取り出すことができます。

datum の項目 gender が “男性” と等しければ

male = male + 1

として1を足していくことで、男性の数をカウントすることができます。

平均評価点を出す

scores = []
for datum in data:
    scores.append(datum["score"])
print 1.0 * sum(scores) / len(scores)

次は、評価点の平均点を算出するプログラムです。
平均は合計/データの個数で求まるので、平均点を格納したリストscoresをつくれば、sum(scores) / len(scores)で算出できますね。

Python 特有の注意点ですが、整数を割り算すると小数点以下が切り捨てになるので、1.0 をかけるようにしてください。参考:Pythonで整数を割り算すると、デフォルトでは小数点以下が切り捨てになる問題の解決方法

まずはscoresをつくるために空のリストscores=[]をつくって、評価点を順々に追加してきます。
for文が終わった後(インデントを戻すと、for文が終了します)、合計/データの個数で平均を求めます。

平均を毎回 sum(scores) / len(scores)と書いて出力するのは面倒なので、できれば avg(scores) みたいな形で出力したいですね。
自分で関数を定義することもできるので、avg()関数を作ってみましょう。

コードの冒頭に以下の2行を追加してください。

def avg(x):
    return 1.0 * sum(x) / len(x)

これは avg() 関数を定義するコードです。

def は関数であることを示しています。
avgは関数の名前、(x)は関数の引数です。関数はこの引数を入力として受け取って加工をし、returnで出力します。(中にはreturnを持たない関数もあります)
あくまで引数は「この関数内ではこの名前で扱うよ」という定義なので、関数に渡す変数と一致させる必要はありません。なので、どんな変数についても汎用的に使うことができます。

最後に、データの入力から平均点の算出までの一連のコードを記します。

# coding: utf8
data = [
    {"name": "田中花子", "gender": "女性", "score": 58},
    {"name": "鈴木一郎", "gender": "男性", "score": 76},
    {"name": "山田太郎", "gender": "男性", "score": 69},
    {"name": "佐藤恵子", "gender": "女性", "score": 62},
    {"name": "石井あや", "gender": "女性", "score": 71}
]

def avg(x):
    return 1.0 * sum(x) / len(x)

scores = []
for datum in data:
    scores.append(datum["score"])
print avg(scores)

課題

以下の3つの課題を解き、ソースコードと出力を提出してください。

  • 形式は問いません(PDF、テキストファイル、スクリーンショット、スライドなど)。ただし、複数ファイルになる場合は、zip形式で圧縮してください。
  • 入門編の課題提出では、paiza.IOや、Tutorial Point(その1その2)などの外部サービスを活用しても構いません。
  • 提出先のメールアドレスは、 gci@weblab.t.u-tokyo.ac.jp です。
  • 課題のレポートには、出力の例も含めてください。

課題1

1900年から2200年までのうるう年を教えてくれるプログラムを書いてください。
ただし、うるう年は下記のルールで決定されるものとします。

  • 西暦が4で割り切れる年は閏年である。
  • ただし、100で割り切れる年は閏年ではない。
  • ただし、400で割り切れる年は閏年である。

課題2

任意の西暦年を渡すと、うるう年か否かを判定してくれる関数を、def機能を使って実装してください。

課題3

お笑いアンケートで観客の生まれた年も聞いてみた結果、以下のデータが集められました。

名前 誕生年
田中花子 1980
鈴木一郎 2000
山田太郎 1989
佐藤恵子 1992
石井あや 1978

うるう年に生まれた観客の平均評価点と、うるう年以外に生まれた観客の平均評価点を求めるプログラムを書いてください。(小数点以下も出力してください。)