[ プログラミング演習(Ruby) >  プログラミング演習(Ruby) 課題(2024) >  [課題18] スクリプトに従った描画 ]

[課題18] スクリプトに従った描画

課題

タートルグラフィクスで,以下で説明するような「スクリプト」に従って, 亀を動作させるプログラム,および適当な絵を描く「スクリプト」を作成して,プログラムとスクリプトを提出して下さい. なお以下で説明する通り,提出するプログラムの先頭に「スクリプトの仕様」を記述しておいて下さい.

「スクリプト」とは亀に対する指令を独自の文法に従って記述したファイルを意味しています. この課題で作成するプログラムでは,亀にスクリプトを読み込ませてスクリプトの記述に従って,亀を動作させるようにします. つまりプログラム内に亀の動作を(タートルグラフィクスの文法で)直接記述する代わりに,別途用意したスクリプトの記述に従って亀が動作するようにするわけです. スクリプトの仕様は以下で説明する条件のもとで自由に定めて下さい.

このプログラムを実現すれば,プログラム自体を書き換えることなく,別途スクリプトを用意することで,(スクリプトの仕様の範囲で)さまざまな図形を描くことができるようになります.

もくじ

  1. スクリプト
  2. 想定する実行方法
  3. 技術要素
  4. プログラムテンプレート
  5. サンプルプログラム
  6. [参考] Symbol

スクリプト

ここではこの課題で実現するスクリプトとはどういうものかを説明します.

スクリプトの例

まずスクリプトの例を示します.


  color 255 0 0
  go 100
  rotate 120
  go 100
  rotate 120
  go 100
  rotate 120

この記述が「タートルグラフィクス」での亀の動作を表していると解釈することは容易でしょう. ここで示したスクリプトは次のような仕様に基づいて作成しています.

スクリプトでの命令実行するタートルグラフィクスの処理
go 距離forward(距離)
rotate 角度turn(角度)
color 赤 緑 青set_color(赤,緑,青)

たとえばスクリプトに「go 100」と書いてあったとすると, 「forward(100)」を実行するわけです.

ここで示したのは飽くまでも一例です. 以下に示す要求事項を満たす限り,どんな機能をどんな形で実現しても構いません.

スクリプトの仕様への要求事項

今回の課題で実現するスクリプトの仕様に求める条件を示します.

  1. 実現する機能
    少なくともタートルグラフィクスの「forward」「turn」「set_color」の機能を実現すること. さらに他の機能を実現することも歓迎する. fill,hover,繰り返し等を実現できると実用性が高まるだろう(実現は簡単ではないだろう).

  2. 描画データの仕様
    長さ,角度,色など描画に使うデータはすべて数値のみを扱えばよい.変数,演算処理はスクリプトの機能として実現しなくてもよい(実現するのはかなり大変だろう).

  3. 文法
    スクリプトの文法は適宜定めてよい(上の例に従う必要はない).

  4. 例外処理
    スクリプトが正しく記述されていない場合の処理(例外処理)は考えなくてもよい(もちろん例外処理を実現してもよい).

[*重要*] スクリプトに関する説明のプログラムへの記述

プログラムで設定したスクリプトの仕様の説明をファイルの冒頭の「=begin〜=end」の間に記述しておいて下さい.


=begin

所属:     
氏名:     
学生番号:
難易度(5段階評価):

スクリプトの仕様

感想など(任意)

=end

上の例に示したように「スクリプトでの命令」と「実行するタートルグラフィクスの処理」を対応付けた説明を書いておくことを想定しています.

スクリプトの作成

今回はスクリプトの例を実際に作成して,プログラムとともに提出してもらうことを想定しています.

スクリプトは,適当な名前のファイルで作成してください. プログラムと同様に「文字」で構成されるファイル(テキストファイル)として作成することになります.

ファイル名には基本的に拡張子をつける習慣があって, ファイル名の拡張子によって,そのファイルを開くアプリケーションを決めることも少なくありません. しかし本質的には拡張子はファイル名の一部に過ぎません. スクリプトを作成するにあたって,拡張子も含めてファイル名は自由に決めてください.

スクリプトを作成するには, プログラムを作成するのと同じアプリケーションで自由な名前でファイルを新規作成して, 自分で定めた文法に従って命令を記述して,適当な名前で保存すればOKです. どんなファイル名であれ,ファイルがスクリプトの文法に従って記述されていれば,それを読み込むことに問題はありません.

想定する実行方法

今回のプログラムは実行するために「スクリプトのファイル」を指定することになります. 指定の方法は次のいずれかとします.

  1. ファイル選択ダイアログを使う
  2. コマンドライン引数を使う

以下,それぞれについて説明します.

ファイル選択ダイアログを使う

プログラム実行中に「ファイル選択ダイアログ」を開いて,スクリプトのファイルを選択させることができます.


require 'gtr/fileselect' # プログラムの先頭にこの行を追加

  :
  :

class Turtle
  def draw
    ## ファイル選択ダイアログによるファイルの選択
    # ファイルが選択された場合はその名前が得られる
    # キャンセルされた場合はnilが得られる
    fname = World.select_file
  

「ファイル選択ダイアログ」はポップアップウインドウでマウスでファイルを選択できるようにするものです. 「ファイル選択ダイアログ」の処理が終わるとファイル名が得られます. それを変数に代入してプログラムで利用できます. 上の例では「fname」に選択されたファイルの名前が代入されます.

なおファイルの選択をキャンセルすることもできます. キャンセルされた場合,ファイル名として「nil」が得られます(nil=何もない). 上の例では「fname」に「nil」が代入されます.

コマンドライン引数を使う

コマンドライン引数(ARGV)でスクリプトのファイルを指定する場合, プログラムは次のように実行することになります. ここではプログラムのファイル名を「gtr_script.rb」として, スクリプトのファイル名を「square.ts」とします.

$ ruby  gtr_script.rb  square.ts

このときコマンドライン引数「square.ts」は次のようにして取り込むことができます.

class Turtle
  def draw

    fname=retrieve() # 記憶させたファイル名を取り出す

  end
end

  :
  :

t = Turtle.new() # 亀を用意する
t.push(*ARGV) # コマンドライン引数を亀に憶えさせる

亀を用意(Turtle.new)した直後に,コマンドライン引数を亀に憶えさせておいて(push), drawメソッドの中でスクリプトのファイル名が必要になった時点で,それをretrieveによって取り出します(上の例ではfnameにファイル名が代入されます).ARGVの前に「*」をつけることに注意して下さい(Rubyでは配列の前に「*」を付けてメソッドの引数に渡すと配列が要素に展開されてメソッドに渡されます).

Rubyでは引数の個数が変わりうるメソッドを定義することができます. Turtleのpushメソッドはそのようなメソッドとして定義してあります.

技術要素

次のようなメソッド等が使えます.必要に応じて使って下さい. 必要でなければ利用しなくても構いません.

プログラムテンプレート

次に示すプログラムのテンプレート(雛型)を使って下さい. このプログラムは名前を適宜変えた上で保存して利用してください.

サンプルプログラム

サンプルプログラムを示します. プログラムをブラウザの画面で開いたときに文字化けしてしまう場合には, ダウンロードしてEmacs等で開いてみて下さい.

[参考] Symbol

ここではRubyの「Symbol」(シンボル)について簡単に説明します. 課題のためには以下を理解する必要はありません.

Symbolは「文字列」(文字が並んだデータ)と似ていますが,異なる種類のものです. Symbolとは「名前」で区別されるデータです. コロン(:)のあとに「名前」を書いて定義します. なお「:」は名前には含まれません.それにつづく部分が名前です.


  :a_symbol # シンボル

上で述べたようにSymbolは文字列(文字の並び)ではありません. 文字列のデータは書き換えられます.一方でSymbolは書き換えられません. 同じ文字の並びで構成される文字列(の実体)はいくつでも作れますが,同一の名前のSymbol(の実体)は一つしかありません.


  #
  # 文字列とそのデータの実体について調べてみる
  #

  s = "ace"   # 文字列
  t = "ace"   # 文字列
  s == t      # ==> true  (s,tの値は等しいか?→等しい)
  s.equal?(t) # ==> false (s,tは同一の実体か?→そうではない.「equal?」はデータの実体の同一性を判定する)
  
  # sの1文字目を"A"に書き換える.
  s[0,1] = "A" # s == "Ace"

  # sへの変更はtには影響しない.つまりs,tの実体は同じではない.たまたま同じ文字が並んでいただけ.

  s == "Ace"  # ==> true  (sの値は"Ace"に等しいか→等しい)
  t == "ace"  # ==> true  (tの値は"ace"に等しいか→等しい)
  s == t      # ==> false (s,tの値は等しいか?→等しくない)
  s.equal?(t) # ==> false (s,tは同一の実体か?→そうではない)

  #
  # Symbolとそのデータの実体について調べてみる
  #
  
  x = :ace # Symbol
  y = :ace # Symbol
  z = :Ace # Symbol

  x == y   # ==> true  (x,yの値は等しいか?→等しい.名前(ace)が同一である)
  x == z   # ==> false (x,zの値は等しいか?→等しくない.名前(ace,Ace)が異なる)

  x.equal?(y) # ==> true  (x,yの実体は同一である)    
  x.equal?(z) # ==> false (x,zの実体は異なる)

    
  #
  # 文字列とそのデータの実体について調べてみる(2)
  #

  t = s        # tをsと同じ文字列データと対応させる
  t.equal?(s)  # ==> true (s,tは同一の実体である)
  t[2,1] = "t" # tの3番目の文字を"t"に書き換える(t=="Act")
  s == "Act"   # ==> true (sの値は"Act"に等しいか→等しい)

[ プログラミング演習(Ruby) >  プログラミング演習(Ruby) 課題(2024) >  [課題18] スクリプトに従った描画 ]