情報理学入門講義案その2

友人に、前回の講義案が褒められたので、調子に乗って第2回

今日のテーマ

前回はグラフを表示しましたが、今回はグラフをアニメーションさせましょう。

何はともあれ、動かしてみる

zipファイルをダウンロードしてください。
解凍すると"anim"フォルダが出来ます。gnuplotで"anim"に移動し、"anim.gp"を「load」します。

gnuplot> cd "D:/python/2/anim"
gnuplot> load "anim.gp"

ここで、

  • 引用符「""」をつける
  • windowsではバックスラッシュ「\」ではなく、スラッシュ「/」を使う
  • 全角・半角を間違えない

に注意。

どうです?動きましたか?

アニメーションする仕組み

anim.gpをテキストエディタで開いてください。

plot "steps/anim_0.txt"
pause 0.05
plot "steps/anim_1.txt"
pause 0.05
plot "steps/anim_2.txt"
pause 0.05
    (中略)
plot "steps/anim_49.txt"
pause 0.05

plot "ファイル名"pause 0.05が並んでいますね。

実は、"steps/anim_**.txt"には、少しずつ変化させたsinのグラフが書き込まれています。

load "anim.gp"で、gnuplotは"anim.gp"のコマンドを次々に実行します。
すると少しずつ変化するグラフが次々に表示されるので、
人間の目には動いているように見えるのです。

pauseは表示速度の調整用です。pause 0.05で0.05秒だけ一時停止します。
pauseが無いと一瞬で終わってしまうので。

ファイルに書き込む

では、アニメーショングラフを作ってみましょう。

グラフのデータをファイルに出力する事は前回学びました。

> generate_graph.py > graph.txt

しかし、今回は50コマ文の、グラフデータが必要です。
一々、リダイレクトで書き込むわけには行きません。

Pythonには(リダイレクトを使わずに)直接ファイルに書き込む機能があります。

ファイルに書き込む
import math
N = 100
with open("sin_graph.txt", "w") as f:
    for i in xrange(N):
        x = 2 * math.pi * i / N
        y = math.sin(x)
        print(x, y, file=f)

"sin_graph.txt"にsinのグラフデータが書き込まれます。

  • with open(ファイル名, "w") as 変数で、ファイルを開きます。
  • 例によってインデントに注意
  • 開いたファイルは、指定した変数(上の例ではf)を通して操作します。
  • ファイルに書き込むために、printにfile=ファイルの変数を付けます。

"w"は"write"の意味です。"read"用の"r"もありますが、それは後で

文字列

しかし、上の例では50個のファイルは作れません。
50個のファイル名を、openに与える方法をまだ知らないからです。

そのためには、"文字列"と、その操作を知らなければなりません。

文字列とは

今あなたが見ているものです。テキスト、文、文字の並び、と言い換えてもいいかも知れません。
アルファベット、ひらがな、カタカナ、漢字、数字、その他記号を扱うものです。

Pythonでは、ダブルコーテーション「""」やシングルコーテーション「''」ではさんだものは、
すべて文字列として扱われます。

文字列の操作

Pythonにおいて、文字列は数と同格です。
つまり、数と同じように、printで表示したり、変数に代入したり、関数に渡したり出来ます。

# 超オーソドックスな入門プログラム。
# これを一番最初に書かせない当講義は異端である
print("Hello World!")
# 足し算は、文字列をつなげる
name = "Tanaka" + " " + "Taro"
print(name) # => Tanaka Taro

# 数を掛けると、繰り返し
print("SPAM" * 10) # => SPAMSPAMSPAMSPAMSPAMSPAMSPAMSPAMSPAMSPAM

# \n で改行、\\でバックスラッシュ
print("I Love \n\\10,000,000")
#=> I Love
#   \10,000,000

# 整数に変換するにはint関数
print(1 + int("2")) # => 3

# 小数に変換するにはfloat関数
print(1 + int("2.5")) # => 3.5

# 文字列に変換するにはstr関数
print("Happy " + str(2011)) # => "Happy 2011"


シングルコーテーションとダブルコーテーションの2種類あるのは、
互いにもう一方の中に書くためで、それ以外の違いはありません。

print("'") # => '

print('"') # => "

print(""") # これはエラーになる

バックスラッシュをつければ、ダブル〜の中にダブル〜を書くことも出来ます。

print("\"") # => "

print('\'') # => '

なお、数<=>文字列は自動で変換されません。つまり、

x = 1 + "hello" #エラーになる
x = 1 + "2"     #数を表現する文字列でもエラー

アニメーションを出力する

今までの事を総合すれば・・・
("steps"ディレクトリは、予め手動で作っておいてください。)

import math
N = 50 #コマ数

# まず、少しずつ違うコマのグラフを出力
for n in range(N):
    filename = "steps/anim_" + str(n) + ".txt"
    with open(filename, "w") as f:
        for i in range(100):
            x = 2*math.pi * i / 100
            y = math.sin(x + 2*math.pi * n / N)
            print(x, y, file=f)

# loadするための、コマンドを羅列したファイルを出力
with open("anim.gp", "w") as f:
    for n in range(N):
        filename = "steps/anim_" + str(n) + ".txt"
        
        # コマンド内のファイル名はダブルコーテーションで囲む
        p = 'plot "' + filename + '"'
        
        print(p, file=f)
        print("pause 0.05", file=f)

どうでしょう?うまくアニメされましたか?

文字列フォーマット

これでアニメーションは自在なわけですが、ちょっとだけ便利な機能を。

先のサンプルはファイル名を作る操作が、やや見にくい。

filename = "steps/anim_" + str(n) + ".txt"

こう書くことが出来ます。

filename = "steps/anim_{0}.txt".format(n)

文字列の後に、.formatをつけると、関数のように呼び出せます。
そして、{0}に、str(n)が挿入された文字列を返します。

さっきの例を書き直す

多少見やすくなったのではないでしょうか?

import math
N = 50 #コマ数

# まず、少しずつ違うコマのグラフを出力
for n in range(N):
    filename = "steps/anim_{0}.txt".format(n)
    with open(filename, "w") as f:
        for i in range(100):
            x = 2*math.pi * i / 100
            y = math.sin(x + 2*math.pi * n / N)
            print(x, y, file=f)

# loadするための、コマンドを羅列したファイルを出力
with open("anim.gp", "w") as f:
    for n in range(N):
        filename = "steps/anim_{0}.txt".format(n)
        p = 'plot "{0}"'.format(filename)
        
        print(p, file=f)
        print("pause 0.05", file=f)
ちょっとだけ詳しく

文字列.formatは、printと同じように、
文字列も(どころか何でも)、また複数個渡せます。

print("{0}/{1}/{2} ({3})".format(2011, 1, 16, "Sunday")) # => 2011/1/16 (Sunday)

{*}は何回でも使えますし、使わない事も出来ます。

print("{0} {2} {0}".format("alpha", "beta", "gamma")) # => alpha gamma alpha

この他にも文字列フォーマットには、表示桁数を指定する記法がありますが、
詳しくはドキュメントを参照。

ファイルオブジェクト

文字列と同じように、ファイルも変数に代入したりできる対象、「ファイルオブジェクト」です。

「ファイル名の文字列」ではなく「ファイル」です。

f = open("graph.txt", "w")
for i in xrange(100):
    print(i / 100, x ** 2, file=f)
f.close()
.close()

.close()は、ファイルを閉じる操作です。
close後はそのファイルオブジェクトは操作不能になります。

openすると、closeするまでの間、他のプログラムがそのファイルを操作できないからです。
プログラムが終了すると、自動でcloseが呼ばれることになっていますが、
ファイルへの書き込みが終わったら、すぐcloseを呼ぶようにしましょう。

with文とopen

with open(ファイル名, "w") as 変数名:は、
実は、ファイルオブジェクトを変数に代入する操作です。

普通に変数に代入するのとの違いは、

  • .close()が自動で呼ばれる
  • インデントで、ファイルを操作しているのが明確になる。

したがって、特に理由が無い限り、withを使ってください。


演習

何か面白いアニメーションを表示してください


つづく