[ プログラミング演習(Ruby) >  プログラミング演習(Ruby) 課題(2024) >  [課題27] パレット ]

[課題27] パレット

課題

タートルグラフィクスで, 以下で説明する仕様に従って, 描画色を管理するオブジェクトのクラスPalette(パレット)を定義し, さらにTurtleにパレットを使って色を扱うメソッドを追加した上で, 追加したTurtleのメソッドを一通りテストするプログラムを作成して 提出してください.

もくじ

  1. プログラムテンプレート
  2. Hash(ハッシュ)
  3. Paletteクラスの仕様
  4. Palette用のTurtleの拡張
  5. サンプルプログラム
  6. [OPTION] ペンの導入
  7. [参考] シンボル

プログラムテンプレート

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

Hash(ハッシュ)

Paletteで色データを管理するためにHash(ハッシュ)オブジェクトを利用します.

ハッシュは整数以外のデータを添字として利用できる配列のようなオブジェクトで, 「キー」(key)を「値」(value)に対応付けた要素の集まりです.


  table = { }  # 空のハッシュ(空の配列と同様のもの)

  # キーと値の対応の登録
  # :red → Symbol
  # 以下の「:名前」も同様にSymbol(Symbolは名前で区別されるオブジェクトです.課題ではSymbolの詳細を理解する必要はありません)
  table[:red] = [255,0,0] # :redに[255,0,0]を対応付ける

  ## ここでtableをpメソッドで表示すると次のようになる
  p table # { :red=>[255,0,0] }

  table[:blue] = [0,0,255] # :blueに[0,0,255]を対応付ける

  # ここでtableをpメソッドで表示すると次のようになる
  p table # { :red=>[255,0,0], :blue=>[0,0,255] }

  # キーに対応する値の参照
  r = table[:red]  # r == [255,0,0]
  b = table[:blue] # b == [0,0,255]

  # 存在しないキーの値を参照→nil
  y = table[:yellow] # y == nil

  # ハッシュに指定したキーが存在しているかどうかを確認(has_key?)
  table.has_key?(:red) # ==> true (:redは存在する)
  table.has_key?(:blue) # ==> true (:blue存在する)
  table.has_key?(:yellow) # ==> false (:yellowは存在しない)

  # キーに対応する値は変更可能
  table[:red] = [224,0,0] # :redに[224,0,0]を対応付ける

  # ここでtableをpメソッドで表示すると次のようになる
  p table # { :red=>[224,0,0], :blue=>[0,0,255] }

上に示したように,ハッシュに存在しないキーの値を参照するとnilが得られます. nilは「偽」として扱われます(他に「偽」として扱われるのはfalseのみ).

[参考] ハッシュのデフォルト値

ハッシュに存在しないキーに対応する値をハッシュのデフォルト値と呼びます. 「ハッシュのデフォルト値」のデフォルト値は「nil」です. デフォルト値を変更することもできます.

  colid = { :red=>1, :blue=>2 } 

  colid[:red]    # ==> 1
  colid[:blue]   # ==> 2
  colid[:yellow] # ==> nil (とくに設定しない限り,存在しないキーに対応するデフォルト値はnil)

  colid.default = -1 # デフォルト値を-1にする

  colid[:red]    # ==> 1
  colid[:blue]   # ==> 2
  colid[:yellow] # ==> -1 (この時点では存在しないキーに対応するデフォルト値は-1)

  # キーが存在しているかどうかはデフォルト値の設定とは無関係  
  colid.has_key?(:red) # ==> true
  colid.has_key?(:blue) # ==> true
  colid.has_key?(:yellow) # ==> false

Paletteクラスの仕様

クラスPaletteで描画色を管理するオブジェクトの仕様を以下のように 定めることにします.

パレットのデータ(インスタンス変数)

パレットのインスタンスメソッド

Palette用のTurtleの拡張

PaletteオブジェクトをTurtleに組み込んで利用できるようにします. そのためにTurtleにインスタンス変数とインスタンスメソッドを追加します.

このような定義を導入する目的は,TurtleのdrawメソッドでPaletteオブジェクトを意識しないで使えるようにすることです. 具体的にはdrawにおいて, Turtle自身の機能であるかのように create_paletteadd_coloruse_color,(あるいは mix_color)を使うことで,適宜Paletteに色を登録したり, 登録した色(あるいはR,G,Bで指定した色,明るさ)を使えるようにしたいわけです(後述の「補足説明」も参照して下さい).

なおcreate_paletteはテンプレートで次のように定義済みです.

add_coloruse_color(とmix_color)メソッドは以下を参照して,定義して下さい.

[補足] Palette用のメソッドをTurtleに定義する意義

Paletteクラスを定義しているにも関わらず, Turtleクラスにcreate_paletteadd_coloruse_color(あるいはmix_color)を定義するのは, 一見回りくどいように思えるかもしれませんが, 上に示したこれらのメソッドの(drawでの)利用例では, @paletteが現れていないことに注目して下さい. これらのメソッドを定義することで,Paletteの存在を一々意識することなく, あたかもTurtle自体の機能として,色を定義して,利用できるようになります. またuse_colorを定義することで,色名と(R,G,B)を区別することなく,色を扱えるようになります.

これらのメソッドが定義されていなければ,色名を利用するたびにPaletteを明示することになります.また名前で定義された色を使う処理が(少し)煩雑になります. 次の例①を見てください.


# 例①: インスタンス変数@paletteを直接使うプログラム
def draw

  set_speed(10)

  @palette = Palette.new([0,0,0])  # パレットを生成
  @palette.add(:red,[255,0,0])     # パレットに:redを登録

  set_weight(2)

  c = @palette.color(:red) # :redに対応する色データを取得
  set_color(c) # 描画色をcに設定
  forward(100)
  turn(90)

  set_color(0,255,0) # 描画色を(0,255,0)に設定
  forward(100)

end
  

それに対して,上で示したような一連のメソッドを定義しておけば,他のTurtleのメソッドと同じように(Paletteを使わないメソッドと区別することなく)処理を書けるようになります. 次の例②を見てください.実行する処理は上の例①と同じです.


# 例②: インスタンス変数@paletteを直接には使わないプログラム
# 上の例①と同じ処理を実現している.@paletteを意識しなくてもよい.
def draw

  set_speed(10)

  create_palette(0,0,0)     # パレットを生成
  add_color(:red,[255,0,0]) # パレットに:redを登録

  set_weight(2)

  use_color(:red) # 描画色を:redに設定
  forward(100)
  turn(90)

  use_color(0,255,0) # 描画色を(0,255,0)に設定
  forward(100)

end
  

[OPTION] ペンの導入

余裕があれば,Paletteに以下に示すような「pen」(ペン)を追加して, Paletteのaddメソッド,colorメソッドと同様に, Paletteに「pen」を追加登録するメソッド,登録されている「pen」を返すメソッドを定義し, さらにuse_colorメソッドと同様に,Turtleに「pen」を指定して使うメソッドを定義してください. それらのメソッドの名前,またメソッドの仕様は自由に決めて, プログラムの先頭の「=begin」「=end」の間に説明を記述してください.

pen

pen(ペン)は色名と線幅によって定まるものとします. 指定する色は,Paletteに登録済みとします(登録済みでない場合は,デフォルトカラーを使うことにします). penには適当な名前(Symbol)をつけて,Paletteに登録して管理することにします. 一旦登録したpenのデータ(色と線幅)は変更しないことにします. Turtleでpenを指定すると,そのpenの色と線幅が以降で使用されるようにします.

サンプルプログラム

次のページにサンプルプログラムを示します. プログラムをブラウザの画面で開いたときに文字化けしてしまう場合には, ダウンロードして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) >  [課題27] パレット ]