課題
タートルグラフィクスで次のように正三角形で構成される図形を描くプログラムを作成して提出してください.
- 中心にある下向きの緑色の正三角形は,一番外側の正三角形の各辺の中点を結んで描いています. 一番小さな正三角形の一辺の長さは,中心にある下向きの緑色の正三角形の半分になっています.
- 図形の大きさと色(2色)は自由に決めてください.上の例に合わせる必要はありません. デザインを調整することを考えて,最初に変数を用意して値を代入しておくとよいでしょう.
この課題ではメソッドを定義,利用してプログラムが簡潔になるように工夫してください. 描画の手順は適宜決めてください.どこからどう描いても構いません.
補足
プログラムを作成するにあたって,次のことを考慮するとよいでしょう.
- 図形を描画するメソッドは「副作用」を伴わないように定義する
図形を描くメソッドにおいて,メソッド実行の前後で亀の状態(位置と向き)が変わる「副作用」があると,メソッド実行の前と後で状態がどのように変わるのか(どんな副作用が発生するのか)を考慮しつつ,図形全体を描画する手順を記述する必要があります.
プログラムでは,自分でメソッドを定義して自分で利用することからピンと来ないかもしれませんが,メソッドを利用する立場で考えれば,副作用がないようにメソッドを定義しておいた方が(メソッド呼び出しも含んだ)描画処理の過程が理解しやすくなることが分かるでしょう(メソッドを使ったとき内部でどのように状態が変化するのか考えなくても済む).
なおメソッドによって生じる副作用(亀の状態変化)が容易に予想できるような場合には 副作用があってもよいでしょう.たとえば『→』という図形を描くとして,『→』の根本から描き始めて,描画を完了した時点で『→』の先端に移動しているのは自然でしょう. - 描画処理が座標系に依存しないようにする
課題では上に挙げたように全体が上向きの正三角形になっている図形が描ければ問題ありません.そのような図形は,座標系(向きと位置)に基づいて描くことができます. しかし,座標系に依存しないように記述すれば,処理の汎用性を高められます.
プログラムテンプレート
次に示すプログラムのテンプレート(雛型)を使ってください. このプログラムは名前を適宜変えた上で保存して利用してください.
描画手順のコメント
プログラムの先頭の「=begin」から「=end」までの間に,描画手順の簡単な説明をコメントとして,追加で書いてください.
=begin
所属:
氏名:
学生番号:
難易度(5段階評価):
感想など(任意)
図形の描画手順:
=end
図形全体のどの位置から描き始めて,どのような三角形をどのような順に描いていくのかを説明してください. 説明の記述のために次のような用語を導入しておきます.
- 大三角形: 図形全体とちょうど重なる正三角形
- 中三角形: 図形の中央にあって大三角形とは逆向きに描かれる正三角形 (1辺の長さは大三角形の半分)
- 小三角形: 図形内の一番小さい正三角形
メソッド定義の作法
プログラムでは,まず最初に「draw」メソッドを定義してから,他のメソッドを定義することを勧めます. 一般には一つのメソッドが階層的に下請けのメソッドを利用するように定義されている場合には,メソッド定義はトップダウンで並べるようにする, つまり階層で上位のメソッドを先に定義して,その後ろに下位のメソッドを定義することを勧めます.
class Turtle
def draw
double_box(a,b) # double_boxメソッドを利用
end
# double_boxメソッドの定義
def double_box(w,h)
w2 = w/2; h2 = h/2
box(w,h) # boxメソッドを利用
box(w2,h2) # boxメソッドを利用
end
# boxメソッドの定義
def box(w,h)
end
end
Rubyでは,メソッドの内部にメソッドを定義しないようにします. 定義を入れ子構造にすることは文法上は可能ですが,Rubyでは意味がありません.
def foo(x)
# fooの中でbarを定義→×
def bar(y)
end
end
定義を入れ子にしても,内部のメソッドでは外部のメソッドのローカル変数は参照できません.
def foo(x)
a = x + 1
def bar(y)
z = y + a # このaは未定義
2*z
end
bar(x*x)
end
foo(2) # Error!
サンプルプログラム
メソッドを用いたサンプルプログラムを用意しています(サンプルコード>メソッド).
これらをブラウザの画面で開いたときに文字化けしてしまう場合には, ダウンロードしてEmacs等で開いてみてください.
Tips
以下,課題に取り組むにあたって,知っておくとよさそうなtips(hint)を示します.
- 同じ計算式を何度も書くのは避ける→変数を適切に活用する
- 変数に代入すればデータを再利用できる
- 変数で記述→プログラムが短くなる
- 何度も計算→計算処理のコストがかかる
- 数値リテラル(数字列)で辻褄合わせするのは避ける→変数を適切に活用する
- 変数の値を変えると辻褄が合わなくなる
- プログラムに汎用性がなくなる
- 数値リテラルはデータの意味が分かりにくい
- 関係性のあるデータは変数の関係式から導出する
- プログラムには適切にインデントを付ける(半角の空白で字下げする)
- プログラムの構造をインデントで視覚的に示す
→構造が見えやすくなって,間違いも見つけやすくなる - Emacsの場合,自動的にインデントが付けられる
インデントがおかしくなった?→上から順に各行で[TAB]
(手動でインデントを付けるのは避ける)
- プログラムの構造をインデントで視覚的に示す
- 数学関数,定数
数学で使われる関数や定数はRubyでは「Math.関数名(パラメタ)」,「Math::定数名」のように記述して使います.sin x Math.sin(x) cos x Math.cos(x) tan x Math.tan(x) xの平方根 Math.sqrt(x) ex Math.exp(x) log x Math.log(x) π Math::PI
三角関数のパラメタはラジアンで与えます. 円周率の定義「Math::PI」の「PI」は大文字のアルファベットです. - タートルグラフィクスでの三角関数
利便性のため,「タートルグラフィクス」の動作の記述においては,次のようにして,「度」(degree)をパラメタに指定する三角関数(sin,cos,tan)が利用できるようになっています.class Turtle def draw d = 100 theta = 45 move(d*cos(theta),d*sin(theta)) # キャンバスのx軸正の方向から左回りでtheta度の方向へd進む end endMath.cos(x),Math.sin(x),Math.tan(x)のパラメタは弧度(radian)ですので注意してください. - markとback(タートルグラフィクス)
- 「mark」とすると,その時点での亀の位置と向きが記憶されます. 亀が自分の目印になるものを配置するイメージです.
- 後で「back」すると,「mark」した場所と向きに戻ります.そのとき線を描画します. 線を描かずに戻るには「hover」と組み合わせます. 「back」すると「mark」はなくなります. 配置した目印を回収するイメージです.
- 「mark」は2つ以上設置できます.2つ以上markされている状態で「back」すると, その時点で最後に設置したmarkに戻ります. backするたびに設置したときと逆順でmarkをたどって戻っていくことになります(回収したmarkはなくなることに注意してください).
s = 100 mark() # マーク1 forward(s); turn(90); forward(s) # 「;」で区切れば1行に複数の式を並べられる mark() # マーク2 turn(30); forward(s); turn(120); forward(s) mark() # マーク3 turn(-120); forward(s); turn(120); forward(s) back() # 線を描きつつマーク3に戻る(マーク3を回収する) back() # 線を描きつつマーク2に戻る(マーク2を回収する) hover { back() } # 線を描かずにマーク1に戻る(マーク1を回収する)
日置尋久(HIOKI Hirohisa)
Last modified: Mon Oct 27 11:18:20 JST 2025