46
2015 9 * 2016.11.30 * 1

情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

Embed Size (px)

Citation preview

Page 1: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

情報科学 2015 久野クラス # 9

久野 靖 ∗

2016.11.30

∗電気通信大学

1

Page 2: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

はじめに長らく「修行」して頂いて来ましたが、今回からは課題はA課題 (当

日課題)のみとなります。今回の内容ですが、プログラム中で多様な

データを表現する手段である動的データ構造について、「単リストを

用いたエディタバッファ」「抽象構文木」の2つの具体例をもとに学ん

で頂きます。

2

Page 3: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

前回の演習問題の解説演習1 — クラス定義の練習この演習はメソッドとインスタンス変数が追加できればよいという

ことで、コードだけ掲載しておきましょう:

class Dog

def initialize(name)

@name = name; @speed = 0.0; @count = 3

end

def talk

puts(’my name is ’ + @name)

end

def addspeed(d)

@speed = @speed + d

puts(’speed = ’ + @speed.to_s)

end

def setcount(c)

@count = c

end

def bark

@count.times do puts(’Vow! ’) end

end

end

3

Page 4: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習2 — 簡単なクラスを書いてみるこの演習はクラスの書き方の練習みたいなものなので、見ていただ

けば十分でしょう。Memory2はちょっと頭を使う必要がありますかね。

class Memory

def initialize()

@mem = nil

end

def put(x)

@mem = x

end

def get()

return @mem

end

end

class Memory2

def initialize()

@mem2 = @mem1 = nil

end

def put(x)

@mem2 = @mem1; @mem1 = x

end

def get()

x = @mem1; @mem1 = @mem2; @mem2 = nil; return x

end

end

4

Page 5: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習2 — 簡単なクラスを書いてみる

class Concat

def initialize

@str = ""

end

def add(s)

@str = @str + s

end

def get()

return @str

end

def reset()

@str = ""

end

end

5

Page 6: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習3 — 有理数クラスこの演習も Ratioクラスのメソッドを「同様に」増やせばよいだけ

なので、難しくはありません。追加するメソッドだけ掲載します:

def -(r)

return Ratio.new(@a*r.get_divisor-r.get_dividend*@b,

@b*r.get_divisor)

end

def *(r)

return Ratio.new(@a*r.get_dividend, @b*r.get_divisor)

end

def /(r)

return Ratio.new(@a*r.get_divisor, @b*r.get_dividend)

end

要は、引き算は足し算と同様、乗算は分母どうし掛け、除算はひっく

り返して掛けるということですね。

6

Page 7: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習4 — 複素数クラス複素数も 2つの値 (実部、虚部)の組なので、有理数によく似ていま

す。ただし個々の値として整数でなく実数を使います。演算はちょっ

と面倒 (とくに除算)ですが、作る時に約分とか分母が 0とか考えなく

てよい部分は簡単になります:

class Comp

def initialize(r = 1.0, i = 0.0)

@re = r; @im = i

end

def get_re

return @re

end

def get_im

return @im

end

7

Page 8: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習4 — 複素数クラス

def to_s

if @im < 0 then

return "#{@re}#{@im}i"

else

return "#{@re}+#{@im}i"

end

end

def +(r)

return Comp.new(@re + r.get_re, @im + r.get_im)

end

def -(r)

return Comp.new(@re - r.get_re, @im - r.get_im)

end

def *(r)

return Comp.new(@re*r.get_re - @im*r.get_im,

@im*r.get_re + @re*r.get_im)

end

def /(r)

norm = (r.get_re**2 + r.get_im**2).to_f

return Comp.new((@re*r.get_re + @im*r.get_im) / norm,

(@im*r.get_re - @re*r.get_im) / norm)

end

end

to sでヘンなことをやっているのは、「a + bi」の形で表示させよう

とした時、虚数部が負の場合には「a− bi」にしたいためです。

8

Page 9: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習6 — モンテカルロ法の誤差答えがすぐ分かる例として、関数 y = xの区間 [0, 1)における積分を

求めてみましょう。答えは両辺が 1の直角 2等辺三角形の面積ですか

ら、0.5であることはすぐ分かります。プログラムは次のとおり:

def integrandom(n)

count = 0

n.times do

x = rand(); y = rand()

if y < x then count = count + 1 end

end

return count / n.to_f

end

では実行させてみます:

irb> integrandom 100

=> 0.55 ←誤差0.05

irb> integrandom 1000

=> 0.475 ←誤差0.025

irb> integrandom 10000

=> 0.4933 ←誤差0.0067

irb> integrandom 100000

=> 0.50127 ←誤差0.00127

irb> integrandom 1000000

=> 0.500674 ←誤差0.000674

試行数が100倍になると誤差が 1

10になるように見えます。これはなぜ

でしょうか。

9

Page 10: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習6 — モンテカルロ法の誤差そもそも、なぜこの方法で面積が求まるのかに立ち帰って考えて見

ましょう。このプログラムでは 1回の試行 (trial — サイコロを振るこ

と)において得られるのは「打った点が関数fの上か下か」つまり「0

か 1 か」の情報だけです。そして上であればcountは増やさず、つま

り0を足し、下であればcountを1増やし、最後にNで割りますから、

この「0か1か」の確率変数の平均を求めているわけです。この確率変

数が 1である確率は関数の面積と等しいので、Nを増やしていけば大数の法則 (law of large number)により、観測される平均値は理論的平均

値 (この場合は関数の面積)に近づいていきます。

では、どれくらい近づくのでしょう。それは中心極限定理(central limit

theorem)が教えてくれます。観測される平均値をX、真の平均をµと

すると、√N (X − µ)

はN(0, 1)つまり平均0、分散1の正規分布に収束します。言い換えれ

ば、誤差を√N倍したものが同じ分布になるのですから、試行数をN

倍にすると誤差は 1√N倍になるわけです。これは確かに上の結果と合

致しています。

10

Page 11: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習7 — 格子上の点で調べる上でやったのと同じ問題を、格子上の点でやってみましょう。簡単の

ため、縦横の分割数を同じ値とし、この値Nを与えるようにしました:

def integgrid(n)

count = 0; d = 1.0/n

n.times do |i|

y = i*d

n.times do |j|

if y <= j*d then count = count + 1 end

end

end

return count / (n**2).to_f

end

動かしてみた結果は次のとおり:

irb> integgrid 10

=> 0.55

irb> integgrid 100

=> 0.505

irb> integgrid 1000

=> 0.5005

じっさいの格子点の数は 2乗なので 100、10,000、1,000,000となって

いることに注意。しかしずいぶん「規則的」な数字に見えます。

11

Page 12: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習7 — 格子上の点で調べるそこで、区間 ( 0

100, 1

100)、( 2

100, 3

100)、…、( 98

100, 99

100)で1、それ以外で0を

値とするというちょっといじわるな関数を考えてみましょう (図 1)。1

1

501

502

503 X

Y

図 1: いじわるな関数

この関数を計算するメソッドと、それを格子点で積分するメソッド

を用意しました:

def oddfunc(x)

t = x*100 % 2

if 0 < t && t < 1 then return 1 end

return 0

end

def integoddgrid(n)

count = 0; d = 1.0/n

n.times do |i|

n.times do |j|

if j*d <= oddfunc(i*d) then count = count + 1 end

end

end

return count / (n**2).to_f

end

1区間の両端は含まれていないことに注意。

12

Page 13: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習7 — 格子上の点で調べる動かしてみると次のとおり:

irb> integoddgrid 10

=> 0.28

irb> integoddgrid 100

=> 0.0496

irb> integoddgrid 1000

=> 0.454546

ほとんど「めちゃくちゃ」ですね…(正しくは0.5になるはずです)。で

は、モンテカルロ法ではどうでしょうか:

def integoddrandom(n)

count = 0

n.times do

x = rand(); y = rand()

if y <= oddfunc(x) then count = count + 1 end

end

return count / n.to_f

end

13

Page 14: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習7 — 格子上の点で調べる実行結果は次の通り:

irb> integoddrandom 100

=> 0.46

irb> integoddrandom 10000

=> 0.4982

irb> integoddrandom 1000000

=> 0.499454

こちらは前と変わらず、試行数をN倍にすると誤差は 1√Nになってい

ます。つまり、格子点だと先のいじわるな関数のように格子のところ

に段差が当たるなどすると偏った結果が出ておかしくなるわけです。

それにそもそも、格子で済むのなら関数の値をもとに普通に台形公式

やシンプソンで積分する方がずっと高速なのでした。というわけで、

「おかしな」関数でもそれなりに計算できるというモンテカルロ法の

利点がお分かりいただけたかと思います。

14

Page 15: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

動的データ構造/再帰的データ構造動的データ構造とその特徴データ構造 (data structure)とは「プログラムが扱うデータのかたち」

を言います。ここでは、プログラムの実行につれて構造を自在に変化

させられる、動的データ構造 (dynamic data structure)について学びま

す。これに対し、ここまでに扱ってきたプログラムのように、それそ

れの変数に決まった形のデータが入っていて、全体の形が変わらない

ものを静的データ構造 (static data structure)と呼びます。一般に、静的 (static)とは「プログラム記述時に決まる」、動的 (dynamic)とは「プ

ログラム実行時に決まる」という意味があります。

動的データ構造は、プログラム言語が持つ「データのありかを指す」

機能を用いて作られます。Rubyでは、複合型 (配列、レコード等)や

一般のオブジェクトの値は実際は、それらのデータやオブジェクトの

ありかを指す参照になっているので、これを利用します。既に忘れている人がいるかもしれませんが、レコードとは複数のフィールドが集まったデータであり、RubyではStruct.newにおいてフィールド名を

表す記号を必要なだけ指定して定義するのでしたね。

15

Page 16: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

動的データ構造とその特徴

This is a pen.

This is a pen.

not

This is a pen.

not

(a)

(b)

(c)

p:

p:

p:

図 2: 単連結リストの動的データ構造

たとえば、次のレコードを見てみましょう:

Cell = Struct.new(:data, :next)

これは2つのフィールドdataとnextを持つCellという名前のレコー

ドを定義していますが、ここで各セルのフィールドnextに「次」のセ

ルへの参照を入れることで、「数珠つなぎ」の動的データ構造を作る

ことができます (図2(a))。2このような「数珠つなぎ」の構造のことを

単連結リスト (single linked list)ないし単リストと呼びます。このCellの使い方は、各CellのnextがまたCellになっていて、自

分の中に自分が入っているように思えます。これは再帰関数と同様で、

このようにデータ型 (構造)の中に自分自身と同じデータ型 (構造)への

参照を含むものを再帰的データ構造 (recursive data structure)と呼びま

す。実際には自分自身が入っているわけではなく図2のように「同種の

データへの参照」が入っているだけですから、何ら問題はありません。

一番最後のところ (アース記号で表している)は「何も入っていない」

という印であるnilが入っています。このあたりも、「簡単な場合は自

分自身を呼ばずにすぐ値が決まる」再帰関数とちょっと似ていますね。

2本当はフィールド data も文字列オブジェクトを参照しているので、文字列を箱の外に描いて矢線で指させるべきなのですが、ごちゃごちゃして見づらくなるのでここでは箱の中に直接描いていています。

16

Page 17: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

動的データ構造とその特徴ところで、動的データ構造だと何がよいのでしょうか? たとえば、図

2(a)で途中に単語「not」を入れたくなったとします。文字列の配列で

あれば、途中に挿入するためには後ろの要素を 1個ずつずらして空い

た場所に入れる必要があります。しかし、単連結リストでは、矢線 (参

照)を(b)のようにつけ替えるだけで挿入ができてしまうのです。逆に、

数単語削除したいような場合も、(c)のように参照のつけ換えで行えま

す。このように、動的データ構造は柔軟な構造の変更が行えるという

特徴を持っています。

参照のつけ替えは、具体的にはどうすればいいのでしょう? 参照と

いうのは要するに「場所を示す値」なので、その値をコピーすること

は「矢印の根本を別の場所にコピーする (矢印自体も 2本になる)と考

えればいいのです。たとえば、図 3では、p.next.nextというのはB

の箱のnextフィールド、p.nextはAの箱のnextフィールドなので、

「p.next = p.next.next」でBの箱を迂回してAの箱の nextフィー

ルドにCの箱を指させることになります。参照を入れてある単独変数

(この例ではp)などでも同様です。

A B

p

C

data next data next data next

A B C

data next data next data next

p

A B C

data next data next data next

p

p.next = p.next.next

p = p.next

図 3: 参照のつけ替え

17

Page 18: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

動的データ構造とその特徴ときに、図 2や 3で使わなくなった箱はどうなるのでしょう? Ruby

では、使われなくなったオブジェクトの領域はごみ集め (garbage col-

lection)と呼ばれる機構によって自動的に回収され、再利用されます。

「使っている」かどうかは、どれかの変数からたどれるかどうかで決

まります。たとえば図2の場合は、先頭のセルを変数pが指していて、

ここからたどれるセルは「使っている」と見なされるのです。

18

Page 19: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

例題: 単連結リストを使ったエディタではここで、単連結リストを使った例題として、簡単なテキストエディタ (text editor)を作ってみましょう。「簡単」なので、編集に使う

コマンドは次のものしかありません:

•「i文字列」—文字列を新しい行として現在位置の直前に挿入する。•「d」 — 現在位置の行を削除する。•「t」 — 先頭行を表示し、そこを現在位置とする。•「p」 — 現在位置の内容を表示する。•「n」または改行 — 現在位置を次の行へ移しその行を表示する。•「q」 — 終了する。

19

Page 20: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

例題: 単連結リストを使ったエディタ実際にこれを使っている様子を示します (すごく面倒そうですが、実

際にこういうプログラムを使ってファイルの編集をしていた時代は実

在しました):

>iThis is a pen. ←挿入

>iThis is not a book. ←挿入

>iHow are you? ←挿入

>t ←先頭へ

This is a pen.

> ←次の行

This is not a book.

> ←次の行

How are you?

> ←次の行

EOF ←おしまい

>t ←再度先頭へ

This is a pen.

>iI am a boy. ←挿入

> ←次の行

This is not a book.

>iWho are you? ←挿入

20

Page 21: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

例題: 単連結リストを使ったエディタ

>t ←再度先頭へ行き全部見る

I am a boy.

>

This is a pen.

>

Who are you?

>

This is not a book.

>

How are you?

>

EOF

>q ←おしまい

これをこれから実現してみましょう。

21

Page 22: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタバッファ以下では、単リストのデータ構造を先頭や現在位置などの各変数も

含めてクラスとしてパッケージします:

class Buffer

Cell = Struct.new(:data, :next)

def initialize

@tail = @cur = Cell.new("EOF", nil)

@head = @prev = Cell.new("", @cur)

end

def atend

return @cur == @tail

end

def top

@prev = @head; @cur = @head.next

end

def forward

if atend then return end

@prev = @cur; @cur = @cur.next

end

def insert(s)

@prev.next = Cell.new(s, @cur); @prev = @prev.next

end

def print

puts(" " + @cur.data)

end

end

レコード定義もクラスに入れることにしました。また、「1つの値を

複数箇所に代入する」のに=を連続して書いてみました。もちろん、2

つの代入に分けても一向に構いません。

22

Page 23: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタバッファこのクラスでは、単リストのセルを上記のCellレコードであらわし、

これを指すための変数として次の 4つを使っています:

• @head—一番先頭に「ダミーの」セルを置き、そのセルを常にこの

変数で指しておく (ダミーがあると、先頭行を削除するのを特別扱

いしないで済ませられるため、プログラムの作成が楽になります)。

• @cur — 「現在行」のセルを指しておく。

• @prev —「現在行の1つ前」のセルを指しておく (挿入や削除の時

にこの変数があるとコードを書くのが楽です)。

• @tail — 一番最後にも「ダミーの」セルを置き、そのセルをこの

変数で指しておく (表示することがあるので内容は「EOF」(end of

file)としてあります)。

initializeでは 2つのダミーセルと上記 4変数を用意します。head

の次がtailであるようにCell.newにパラメタを渡していることにも

注意。

23

Page 24: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタバッファ

EOF

How are

old

head: prev: cur:

you?areHowtop

EOF

head: prev: cur:

you?areHowforward

you?

cur:prev:head:

insert EOF

How are

old

you?

head:

forward EOF

prev: cur:

How are

old

you?

head:

EOF

prev: cur:

delete

tail:

tail:

tail:

tail:

tail:

図 4: エディタバッファに対する操作

では次に、メソッドを見てみましょう。図4に、適当なバッファの状

態で操作を行った例を示します (最後の deleteは課題の参考用です)。

atendは現在行が末尾にあるか (@tailと等しいか)を調べます。top

は@prevと@curを先頭に設定します。forwardは@prevと@curを 1つ

先に進めますが、現在行が@tailの時は「果て」なので何もしません。

printは現在行の文字列を表示します。insertは新しいセルが@prev

となり、元の@prevのセルの次が新しいセル、新しい@prevの次が@cur

のセルとなります。これを動かした様子を見てみましょう: 3

3irb の結果表示はうるさいので省略しています。

24

Page 25: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタバッファ

irb> e = Buffer.new

=> ...

irb> e.insert(’abc’)

=> ...

irb> e.insert(’def’)

=> ...

irb> e.insert(’ghi’)

=> ...

irb> e.top

=> nil

irb> e.print

abc

=> ...

irb> e.forward

=> ...

irb> e.print

def

=> nil

確かに文字列が順序どおり挿入でき、それをたどることができてい

ます。このクラスは「行の挿入や削除が自在にできる機能を持ったオ

ブジェクト」を作り出しています。内部では込み入ったデータ構造を

管理していますが、その様子はクラスの外側からは見えません。この

ように内部構造はカプセル化によって隠して、操作を通じて整合性のある汎用的な機能を提供するものを、抽象データ型 (abstract data type

— ADT)と呼びます。

クラス方式のオブジェクト指向言語では、抽象データ型はクラスに

よって定義するのが自然です。前章に出てきた有理数クラスや複素数

クラスも抽象データ型の例だといえます。

25

Page 26: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

演習1図5は「How」という行と「are」という行の間に「old」という

行を挿入する様子 (A→B)、および、「old」「are」「you?」という 3

行のうちから「are」を削除する様子 (B→C)を示しています。資

料 (ないし同じ図を描き写したもの)の上に赤ペンで次のものを記

入しなさい。

a. (A)の図の上に、(A)から (B)につながりが変化するための矢線

のつけ替えを、(1)、(2)のようにつけ替えを行う順番つきで記入

しなさい。ただし、矢印のつけ替えを行う時には、その出発点

がどれかの変数そのものであるか、またはどれかの変数から矢

線でたどれる箱であることが必要である。

b. (B)の図の上に、(B)から (C)につながりが変化するための矢線

のつけ替えを、(1)、(2)のようにつけ替えを行う順番つきで記入

しなさい。ただし、矢印のつけ替えを行う時には、その出発点

がどれかの変数そのものであるか、またはどれかの変数から矢

線でたどれる箱であることが必要である。

How are

How old are

prev cur

prev cur

old

(A)

(B) you?

How old are

prev cur

(C) you?

図 5: 挿入と削除のようす

26

Page 27: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタバッファ

演習2クラス Bufferを打ち込み、動作を確認せよ。動いたら、以下

の操作 (メソッド)を追加してみよ。

a.現在行を削除する (EOF行は削除しないように注意…)

b. 現在行と次の行の順序を交換する (EOFは交換しないように…)

c. 1つ前の行に戻る (実は大変かも)

d. すべての行の順番を逆順にする (かなり過激)

演習3単方向リストでは各セルが「次」の要素への参照だけを保持し

ていたが、各セルが「次」と「前」2つの参照を持つようなリスト

もある。これを双連結リスト (double linked list)ないし双リストと呼ぶ。編集バッファの双リスト版を作り、その得失を検討せよ。4

4ちなみに、双リストなら単リストでの「頭」と「最後」を 1 つで兼ねることもできます。無理に兼ねなくてもよいですが。

27

Page 28: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタドライババッファのメソッドを呼ぶだけでも編集はできますが、面倒です。先

にお見せしたように「コマンド (+パラメタ)」ですらすら編集ができ

るように、エディタとして動作するコードも作ってみました。内容は

とても簡単で、バッファを生成し、その後無限ループでプロンプトを

出し、1行読んでは先頭の1文字でどのコマンドを実行するか枝分かれ

します (コメントにしてあるのはあなたが作るか、後で機能を追加す

るためのものです):

def edit

e = Buffer.new

while true do

printf(">")

line = gets; c = line[0..0]; s = line[1..-2]

if c == "q" then return

elsif c == "t" then e.top; e.print

elsif c == "p" then e.print

elsif c == "i" then e.insert(s)

# elsif c == "r" then e.read(s)

# elsif c == "w" then e.save(s)

# elsif c == "s" then e.subst(s); e.print

# elsif c == "d" then e.delete

else e.forward; e.print

end

end

end

28

Page 29: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタドライバ文字列の一部を取り出すには「[位置..位置]」という添字指定を使

います。1文字だけの場合でも文字列として取り出したい場合は位置

を 2つ指定するので、先頭の文字は line[0..0]で取り出しているわ

けです。なお、Ruby 1.9からは位置 1つだけでも文字列として取り出

せるようになりましたが、それ以前は位置 1つだけだと「文字コード(character code)を表す数値」が返されます。

i(insert)コマンド等では、2文字目から最後の文字の手前まで (最後

の文字は改行文字)も必要なので、これも取り出しています。Rubyで

は文字列や配列中の位置として負の整数を指定すると末尾からの位置

指定になります。

どのコマンドでもない場合 (や改行だけの場合)はいちばんよく使う

「1 行進んで表示」にしました。

29

Page 30: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

エディタドライバ

演習4エディタドライバを打ち込んで先のクラスと組み合わせて動作

を確認せよ。動いたら以下のような改良を試みよ (クラス側を併せ

て改良しても、このメソッドだけを改良しても、どちらでも構いま

せん。文字列を数値にする必要が生じたら、メソッドto iを使っ

てください)。

a.演習 1で追加した機能が使えるようにコマンドを増やす。

b. 現在行の「次に」新しい行を追加するコマンド「a」を作る (追

加した行が新たな現在行になるようにしてください)。

c. 現在行の内容をそっくり打ち直すコマンド「r」を作る。

d. 「g行数」で指定した行へ行くコマンド「g」を作る。

e. コマンド「p」を「p行数」でその行数ぶん打ち出すように改良

(その際、できれば現在位置は変更しないほうが望ましいです)。

f.その他、自分が使うのに便利だと思うコマンドを作る。

30

Page 31: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

文字列置換とファイル入出力せっかくエディタができたのに、行内の置き換えとかファイルの読

み書きができないと実用になりませんから、これらを一応解説してお

きます。

まず、行内の置き換は「s/α/β/」により現在行中の部分文字列α

をβに置き換えるというコマンドにしました。エディタドライバから

はバッファのメソッドsubstを呼ぶだけとしたので、こちらの中身を

示します:

def subst(str)

if atend then return end

a = str.split(’/’)

@cur.data[Regexp.new(a[1])] = a[2]

end

文字列のメソッドsplitは、渡されたパラメタ「/」のところで文字列

を分割した配列を返します。その 1番目をRegexp(パターン)オブジェ

クトに変換して文字列に添字アクセスすると、そのパターンの箇所が

あれば、代入によりそこを別の文字列に置き換えられます。

31

Page 32: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

文字列置換とファイル入出力ファイルの読み書きみは、# 5で学んだopenでファイルを開き、読

む場合は付属ブロック内でそのファイルの各行をinsert、書く場合は

逆にバッファの各行をファイルにputsで書き出します:

def read(file)

open(file, "r") do |f|

f.each do |s| insert(s) end

end

end

def save(file)

top

open(file, "w") do |f|

while not atend do f.puts(@cur.data); forward end

end

end

演習5自分が改良したエディタでどれか1課題ぶん全部の編集を行い、

体験を述べよ。エディタの機能として何があれば必要十分なのか、

エディタの使いやすさは何によって決まるかについて考察するこ

と。5

演習6動的データ構造を活用した、何か面白いプログラムを作れ。面

白さの定義は各自に任されます。

5なお、エディタのバグによりせっかく作ったプログラムがぐちゃぐちゃになるなどの被害に逢ったとしても、当局は一切関知しませんので、そのつもりでお願いします。

32

Page 33: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木とその実現抽象構文木/式木前節の単リストでは「次」の要素が1つだけでしたが、これが2つ以

上の場合、ある「親」の下に複数の「子」がぶらさがっている、という

捉え方ができます。そしてこのような構造を (複数の子が「枝分かれし

ている」という類推から)木構造 (tree)と呼びます。いちばん大元の親

も同様に根 (root)、逆に子のない先端を葉 (leaf)と呼びますが、ただし

情報科学では普通の木とは上下さかさまに、根を一番上に描きます。

木構造の用途の 1つとして、プログラミング言語による記述の構造

を表す、というものがあります。たとえば「x + 1」という式は、+と

いう (加算の)ノードの左右にx(変数ノード)と1(定数ノード)がぶら下

がった木構造で表現できます (図 6左)。このような、プログラミング

言語の構文 (syntax)に対応した木構造 (正確に言えばそれをさらに簡潔

に整理したもの)を抽象構文木 (abstract syntax tree)、その中で式に対応するようなものを式木 (expression tree)と呼びます。

x 1

+

3

+

x 2

* 2

*

3 x

+

x + 1 3 + x * 2 (3 + x) * 2

図 6: 演算式の抽象構文木 (式木)

もう少し複雑な「3 + x * 2」「(3 + x) * 2」を考えると、前者はx

に2を掛けて3を足し、後者はxと3を足してから2を掛けることにな

ります。これらの演算順序の違いも、木構造では素直に表せます (図6

中・右)。つまり、通常の数式では「乗除算は加減算より優先」「かっ

こ内は先に計算」などの規則がありますが、これらは「1列に」式を

表す時に必要な解釈の規則であって、式木になった状態では演算順序

は木の構造によって表されています。

33

Page 34: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木のオブジェクト表現/動的分配抽象構文木をオブジェクトの集まりで表現してみましょう。先の単連

結リストでは各ノードをレコードで表現しましたが、こんどは「加算」

「乗算」「変数」「定数」など各種のノードを1つのオブジェクトとして

表現するので、ノードの種別ごとに 1つのクラスを用意します。そし

て変数の値はグローバルなハッシュ$varsに、変数名のエントリにそ

の変数の値を格納する形で保持します。6。1行目がその初期化です。

$vars = {}

class Add

def initialize(l, r) @left = l; @right = r end

def exec() return @left.exec + @right.exec end

def to_s() return ’(’[email protected]_s+’ + ’[email protected]_s+’)’ end

end

class Mul

def initialize(l, r) @left = l; @right = r end

def exec() return @left.exec * @right.exec end

def to_s() return ’(’[email protected]_s+’ * ’[email protected]_s+’)’ end

end

class Lit

def initialize(v) @left = v end

def exec() return @left end

def to_s() return @left.to_s end

end

class Var

def initialize(v) @left = v end

def exec() return $vars[@left] end

def to_s() return @left.to_s end

end

6ハッシュについては次回扱いますが、とりあえず「文字列が添字にできる配列のようなもの」と思ってください

34

Page 35: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木のオブジェクト表現/動的分配クラスAddとMulは加算と乗算のノードを表します。これらは作成時

に左右の子供を受け取り、インスタンス変数@leftと@rightに格納し

ます。メソッドexecは計算を実行して結果を返するもので、左と右そ

れぞれの計算を実行した後、その結果を加算/乗算したものを返しま

す。to sは文字列表現を返すので、左の子と右の子の文字列表現を+

や*でつないだものを返します。ただし、入れ子関係が分からなくな

ると困るので、全体をかっこで囲むようにしています。

クラスLitとVarは定数と変数のノードを表現します。作成時に定数

はその値、変数はその名前文字列を受け取り、インスタンス変数@left

に格納します(各ノードで変数名をできるだけ揃えるようにしました)。

execは定数では値を返すだけですが、変数では$varsに格納されてい

る値を取り出して返します。to sは変数や定数の文字列を返すだけで

す。

ここで、@left.execのようなメソッド呼び出しは、@leftに現在何

が入っているかに応じて、そのオブジェクトのメソッドを呼び出すこ

とに注意してください。たとえば、@left がAddオブジェクトなら足

し算、Varオブジェクトなら変数値の参照が行われて、その結果が返

されます。

このように、「実行時に実際に使われているものに応じてメソッドが

選択される」機能のことを動的束縛(dynamic binding)ないし多態(poly-

morphism)と言い、オブジェクト指向言語に不可欠な機能です。この

おかげで、式の中身が何であっても「実行する」とだけ言えばその内

容に応じて動作してくれるわけです。

35

Page 36: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木のオブジェクト表現/動的分配では実際に動作させてみましょう。

irb> $vars[’x’] = 5

=> 5

irb> e = Add.new(Var.new(’x’), Lit.new(1))

=> ...

irb> puts(e, e.exec)

(x + 1)

6

=> nil

irb> f = Add.new(Lit.new(3), Mul.new(Var.new(’x’), Lit.new(2)))

=> ...

irb> puts(f, f.exec)

(3 + (x * 2))

13

=> nil

irb> g = Mul.new(Add.new(Var.new(’x’), Lit.new(3)), Lit.new(2))

=> ...

irb> puts(g, g.exec)

((x + 3) * 2)

16

これは、変数xの値を5とし、あとは図6の3つの式木を組み立て、そ

れを表示した後、実行して結果を打ち出しているものです。確かに正

しく実行 (計算)できていますね。

36

Page 37: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木のオブジェクト表現/動的分配

演習7上の例題を打ち込み、式の内容は変えて、正しく計算できるこ

とを確認せよ。できたら、以下のようなノードのクラスを追加し、

動作を確認せよ (to sでの表現方法は適当に決めてよい)。

a.引き算のノードSub、除算のノードDiv。

b. 代入Assign。このノードをexecすると、左のノード (Varであ

ることを前提)の文字列表現 sをキーとして配列$vars[s] に右

のノードをexecした結果を書き込む。結果としては、書き込ん

だのと同じ値を返す。次のようになるはず。

irb> e = Assign.new(Var.new(’x’), Add.new(Var.new(’x’), Lit.new(1)))

=> ...

irb> e.exec

=> 6

irb> $vars[’x’]

=> 6 ←5だったのが6に増える

c. 反復Loop。このノードをexecすると、左のノードをexecして

得た回数だけ、右のノードを繰り返しexecする (結果は何を返

してもよい)。次のようになるはず。

irb> f = Loop.new(Lit.new(20), e) ←eは上で用意した x = x

=> ...

irb> f.exec

=> ...

irb> $vars[’x’]

=> 26 ←20回1ふやしたので26

37

Page 38: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木のオブジェクト表現/動的分配d. 連続Seq。このノードをexecすると、左の子ノード、右の子ノー

ドの順にexecする。結果としては、右の子ノードをexecした

結果を返すものとします。次のようになるはず。

irb> g = Seq.new(f, f) ←fは上で用意した 20回のx = x + 1

=> ...

irb> g.exec

=> 46

irb> $vars[’x’]

=> 46 ←20回ふやすことを2回連続したので46

38

Page 39: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

抽象構文木によるプログラムの表現継承によるくくり出し前節の例題を見て、AddとMulのコードはとても似ていると思いませ

んでしたか。実は、クラスを沢山作り始めると、しばしばこのような

ことが起きます。このような時、クラス方式のオブジェクト指向言語

では継承 (inheritance)と呼ばれる機能を使うことで、記述を簡潔にで

きます。継承とは次のような機能を言います:

•クラスを定義する時、既にある別のクラスを土台として新しいク

ラスを定義できる。土台にするクラスを親クラス (parent class)な

いしスーパクラス (superclass)、新しく作るクラスの方を子クラス(child class)ないしサブクラス (subclass)と呼ぶ。

•子クラスは親クラスのインスタンス変数、メソッド定義をそっくり

そのまま引き継ぐ (「継承」の意味)。

•子クラスで独自のインスタンス変数やメソッド定義を追加できる。

•子クラスで親クラスと同名のメソッドを定義することで、親クラス

のメソッドを差し換えることができる。これをオーバライド (over-

ride)と呼ぶ。

39

Page 40: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

継承によるくくり出し先のコードを継承を用いて構造を整理してみましょう。まず、全ノー

ドの土台となるクラスNodeを用意し、共通のメソッド (ここではto s

だけですが)を用意します:

$vars = {}

class Node

def initialize(l=nil, r=nil)

@left = l; @right = r; @op = ’?’

end

def to_s()

return ’(’ + @left.to_s + @op.to_s + @right.to_s + ’)’

end

end

@left、@rightが左と右の子というのは同じですが、さらに表示用

の演算子 (operator — +、/などの演算記号のこと)を入れるインスタン

ス変数@opも追加しました。

これを土台に Addをサブクラスとして定義します (Rubyでは「< 親

クラス」という指定があるとそのクラスが指定した親クラスのサブク

ラスとなります):

class Add < Node

def initialize(l, r) super; @op = ’+’ end

def exec() return @left.exec + @right.exec end

end

このサブクラスではinitializeとexecだけを定義していて、前者

は親クラスにあるメソッドなのでオーバライド (差し換え) になりま

す。initializeの最初にあるsuperというのは、親クラスの同名のメ

ソッドを (同じ引数で)呼び出すという意味になり、これにより@left、

@rightの初期化が正しく行えます。

40

Page 41: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

継承によるくくり出しもし引数を変更したい場合は、(...)で独自の引数を指定することも

できます。@opについては’+’を自分で入れています。exec について

はこれまでの (継承を使わない)版と同じですね。以下同様にして、他

のノードも定義してみましょう:

class Sub < Node

def initialize(l, r) super; @op = ’-’ end

def exec() return @left.exec - @right.exec end

end

class Mul < Node

def initialize(l, r) super; @op = ’*’ end

def exec() return @left.exec * @right.exec end

end

class Div < Node

def initialize(l, r) super; @op = ’/’ end

def exec() return @left.exec / @right.exec end

end

class Lit < Node

def exec() return @left end

def to_s() return @left.to_s end

end

class Var < Lit

def exec() return $vars[@left] end

end

LitとVarについては、自前でto sを定義することにしたので、initiailze

はオーバライドしていません (@op に固有のものを入れる必要がない

ため)。そしてさらに、VarはLitから継承することで、exec()のみ定

義すればよいようにしました。

41

Page 42: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

制御構造の実現もっと違った動作のノードとして、「代入」「連接」「ループ」「何も

しない」を用意しました。代入は、左辺が変数であるものとして、そ

の@leftに変数文字列が入っているはずですから、$varsのその変数

名のエントリに右辺の値を格納します:

class Assign < Node

def initialize(l, r) super; @op = ’=’ end

def exec() v = @right.exec; $vars[@left.to_s] = v; return v end

end

連接は、左を実行して、それから右を実行し、その結果を返すだけ

です。:

class Seq < Node

def initialize(l, r) super; @op = ’;’ end

def exec() @left.exec; return @right.exec end

end

ループは左を実行した結果の回数だけ右を繰り返し実行します。最

後に実行した値を返すため変数vに毎回値を保存しています (0回実行

の場合は 0が返ります):

class Loop < Node

def initialize(l, r) super; @op = ’L’ end

def exec()

v=0; @left.exec.times do [email protected] end; return v

end

end

42

Page 43: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

制御構造の実現「何もしない」というのは後で使うものですが、to s もオーバライ

ドしてただの「?」を返すようにしてあります。:

class Noop < Node

def exec() return 0 end

def to_s() return ’?’ end

end

ではちょっと試してみましょう。以下は、nに5を入れ、xに1を入れ、

nの回数だけ繰り返し、x = x * nと n = n - 1を実行し、最後に x

の値を全体の結果とする (つまり 5の階乗を計算する)コードの抽象構

文木を組み立て、印刷して、実行します:

def test1

e =

Seq.new(

Assign.new(Var.new(’n’), Lit.new(5)),

Seq.new(

Assign.new(Var.new(’x’), Lit.new(1)),

Seq.new(

Loop.new(

Var.new(’n’),

Seq.new(

Assign.new(Var.new(’x’), Mul.new(Var.new(’x’),

Var.new(’n’))),

Assign.new(Var.new(’n’), Sub.new(Var.new(’n’),

Lit.new(1))))),

Var.new(’x’))))

puts(e)

return e.exec

end

43

Page 44: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

制御構造の実現では実行してみましょう:

irb> test1

((n=5);((x=1);((nL((x=(x*n));(n=(n-1))));x)))

=> 120

確かに、ちゃんと階乗が計算できています。表示はちょっと読みにく

いですが…

このように、プログラムの構造を内部的に表現し、それを「実行」す

ることで、「プログラムの記述どおりの動作を行うプログラム」が作れ

ます。一般に「プログラムの記述どおりの動作を行うプログラム」の

ことをインタプリタ (interpreter — 解釈実行系)と呼びます。

ここで示したような抽象構文木を直接解釈するタイプのインタプリ

タは作りやすく、それなりに使われています (ただし遅いという弱点

があります)。たとえばRubyのバージョン1.8.xまでの実行系はこのタ

イプのインタプリタを用いています。

演習8ノードの種別として次のようなものを増やしてみよ。

a.大小比較の演算子。ここでは値を全部整数としているので、た

とえば「x < y」は条件の成否に応じて1または0を結果として

持つ、ということにするとよい。

b. while文。条件部分は「0がfalse、それ以外はすべてtrue」と

して扱うものとする。

c. if文。とりあえず then部だけでよいが、頑張ってelse部も書ける

ようにしたければそうしてもよい。

d. 絶対値、2数の最大、最小などの演算子。

e. その他、目新しい/面白い機能を持った文や演算。

演習9抽象構文木の考え方を活かした面白いプログラムを作ってみよ。

面白さの定義は各自に任されます。

44

Page 45: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

本日の課題9A演習2以降のどの演習でもいいので、動かしたプログラムを含む小レ

ポートを今日中に久野までメールで送ってください。

1. Subject: は「Report 9A」とする。2.学籍番号、氏名、ペアの学籍番号 (または「個人作業」)、投稿日時

を書く。3.プログラムどれか 1つのソース。4.以下のアンケートの回答。

Q1. 動的データ構造とはどのようなものか理解しましたか。

Q2. プログラムのようなものの構造を木構造で表すというやり方に

納得しましたか。

Q3. 本日の全体的な感想と今後の要望をお書きください。

45

Page 46: 情報科学 2015 久野クラス #9 - lecture.ecc.u ...lecture.ecc.u-tokyo.ac.jp/~ckuno/is16/siryou/ohp9.pdf · とした時、虚数部が負の場合には「a−bi ... 機能を用いて作られます。Rubyでは、複合型(配列、レコード等)

次回までの課題9B次回までの課題はありませんが、よろしければ気に入った演習問題

の解答+考察をこれまで通り「Report 9B」のようなタイトルで提出し

てください。サイトに掲載し、講評を記載します。ただし加点はあり

ません。

46