[ プログラミング演習(Ruby) >  プログラミング演習(Ruby) 課題(2024) >  [課題26] プログレスバー ]

[課題26] プログレスバー

課題

以下の説明にしたがって,擬似的な「プログレスバー」をインタラクティブ(interactive; 対話的)に操作するプログラムを作成して,プログラムを提出してください.

もくじ

  1. プログラムテンプレート
  2. プログレスバー
  3. 「REPL」によるプログレスバーのインタラクティブな操作
  4. [参考] 端末上で色の表示
  5. [参考] Symbol

作成するプログラムでは, プログラム実行後に「プロンプト」を表示して,そこで指定される命令に基づいて プログレスバーをインタラクティブ(interactive;対話的)に操作することを想定しています. 要するに,この授業で利用している「Terminal」のようにプログラムを動作させるわけです.

プログラムテンプレート

次に示すプログラムのテンプレート(雛型)を適宜名前を変えて使って下さい.

テンプレートはそのまま実行できます(ファイル名をprogress.rbと変更したものとします)

  $ ruby  progress.rb

実行すると「Terminal(端末)」と同様に,次のような「プロンプト」が表示されます.

  >>

このプロンプトにつづいて,何か適当に文字列を入力して最後に[Enter]を押すと, 次のように入力した文字列がオウム返しされます.

  >> hello! ⏎
  input = 'hello!'

このとき,プログラムではプログレスバー(ProgressBar)のechoメソッドが実行されています.

入力は何度でも受け付けられます. Terminalでの操作と同様に履歴も使えます.

  >> [↑]

[Ctrl]+[d]([Ctrl]を押しながら[d]を押す)を入力すると,プログラムを終了します.

  >> [Ctrl]+[d]
  exited
  $ 

このテンプレートで「Terminal」のようにインタラクティブな処理を行う仕組みの基盤が提供されていることが分かるでしょう.

今回のプログラムは,このテンプレートに基づいて作成してください. テンプレートに記述されている定義,処理の詳細については,以下で確認してください.

プログレスバー

一般にプログレスバーとは,コンピュータで時間のかかる処理の実行中に, その進行状況を逐次表示するものです. この課題では,次のような擬似的なプログレスバー(Bar)を考えます.

次にプログラムを実行した後, プログレスバーを起動して,すべてのUNITを表示し終えた状態の例を示します. この例ではBarを構成するUNITの個数は20(n=20)としています. UNITは「#」で表示しています. また「go」という命令によって, プログレスバーが起動して,動作が開始されることにしています. プログレスバーの動作が終わった後は,次のプロンプトが表示されています. 最後に[Ctrl]+[d]と入力して,プログラムの実行を終了しています.


  $ ruby progress.rb
  >> go ⏎
  ####################
  >> 
  >> [Ctrl]+[d]
  exited
  $

課題のプログラムでは,プログレスバーのクラスを定義して, そのインスタンスを利用して, インタラクティブな操作を行うことにします. テンプレートでは次のようにクラスの雛形を提供してます.


  ## [課題] ProgressBarクラスを完成させてください
  class ProgressBar
    # n: barを構成するunit数
    # b: 更新の間隔(as BPM)
    def initialize(n,b)

    end

        :
        :

  end

インスタンス変数は適宜導入してください. 必要ならクラス定数も導入してください.

プログレスバーのメソッド

プログレスバーには,少なくとも以下のメソッドを定義することにします(テンプレートには空の定義を最初から組み込んであります). これらのメソッドをインタラクティブな操作(命令)によって呼び出して, プログレスバーの処理を実行するようにします.

これら以外のメソッドを追加することも歓迎します.

プログラム実行の一時停止(sleep)

時間間隔を空けて,プログレスバーのunitを表示させるには, sleepでプログラムを一時停止させます.


   sleep(s) # s秒一時停止する(1秒未満を指定することも可能)

停止させる時間は秒単位で指定します. 指定する時間は整数でなくても構いません.1秒未満を指定することもできます.

「REPL」によるプログレスバーのインタラクティブな操作

REPLとは, 「Read-Eval-Print Loop」の略で, コマンドを入力,実行して,結果を示すという処理の繰り返しを意味しています. 授業で利用しているTerminal(端末)でもお馴染みのものです. REPLによる操作は,コマンドを一つずつ入力して,その結果を見ながら,順に作業を進めていくスタイルから,インタラクティブ(interactive;対話的)と言われます.

REPLの「コマンド入力→実行→結果の表示」の繰り返し処理は次のように表現できます.

  1. コマンドを読み取る(Read)
  2. コマンドを評価(処理)する(Eval)
  3. 処理の結果を表示する(Print)
  4. 1-3を終了の指示があるまで繰り返す(Loop)

テンプレートでは, 次のようにして,まずプログレスバーのインスタンスを生成してから, REPLに入るようになっています.


  PROMPT='>> '    # 実行中に表示するプロンプト(変更してもよい)
  DEFAULT_FULL=20 # バーを構成するUNITの個数の初期値
  DEFAULT_BPM=240 # 更新間隔の初期値(BPM: Beats Per Minute)

  # プログレスバーのインスタンスを生成する
  pbar = ProgressBar.new(DEFAULT_FULL,DEFAULT_BPM)

  ## [課題] プログレスバーを操作するREPL(Read-Eval-Print Loop)を
  ##        完成させてください
  ##        (上で生成しているpbarがプログレスバーのインスタンスです)

  # loop do ... endは無限ループ(無条件の繰り返し)
  loop do
    # プロンプトを表示して1行読み取る
    # → buffにキーボードから読み込んだ文字列が代入される
    #   (※ Terminal同様に履歴が使える)
    buff = Readline.readline(PROMPT,true)

    # [Ctrl]+[d]が入力されたらloopから脱出して終了
    # (そのときbuff==nilとなる)
    break unless buff

    # 読み取った行を表示する(テスト用)
    pbar.echo(buff)
  end
  puts "\nexited"

REPLの動作は「テンプレート」で確認したとおりです.

今回のREPLではコマンドを次のような形式で指定することで, プログレスバーのメソッドのうち,「launch」「full!」「bpm!」に対応する操作が実行できるようにしてください.

  # コマンドの仕様(例)
  go           # ProgressBarのインスタンスのlaunchを実行
  set 30       # ProgressBarのインスタンスのfull!(30)を実行
  tempo 120    # ProgressBarのインスタンスのbpm!(120)を実行

コマンド名は,必ずしもこの通りにしなくても構いません.自由に決めてください. ProgressBarクラスに「launch」「full!」「bpm!」以外にも(public)メソッドを 定義した場合には,それらを利用するコマンドも追加してください.

コマンドの解釈と実行

REPLの実行中に,キーボードから読み取られた命令(buffに格納)を解釈して, 命令にしたがって,プログレスバーのメソッドを実行します. プログレスバーが起動(launch)されたら, bpmに合わせてUNITをすべて順に表示します. UNITの個数の変更,bpmの変更が指示されたら,それにしたがって, プログレスバーのデータを更新します.

技術要素

プログラムを作成するために,以下を参考にしてください.

[参考] 端末上で色の表示

端末(Terminal)上の文字,文字の背景に色をつけることができます. 課題として必須ではありませんが,色を使う場合には以下を参考にしてください.


  require 'ansi_sgr'  # プログラムの先頭に追加

  # 色の付け方の仕様例(文字→赤色,背景→黄色)
  spec={ :fg=>:red, :bg=>:yellow }

  msg = ANSISgr.sgr('Hello, World!',spec) # specの仕様にしたがって色指定をした文字列(Hello, World!)を生成
  puts msg # 端末画面への表示(最後に改行)
  

上の例で示したように文字色と背景色は次の形式でHashで指定します.


  # fg→文字色  
  # bg→背景色  
  color = { :fg=>色名, :bg=>色名 }

文字色(:fg)のみ,背景色(:bg)のみを指定することもできます.

:fg」「:bg」はSymbolです.それぞれ文字色,背景色を指定するためのキーワードで,そのまま記述します.

利用する色もSymbolで指定します. 次のいずれかを文字色あるいは背景色に指定可能です.

:black
:red
:green
:yellow
:blue
:magenta
:cyan
:white

[参考] 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) >  [課題26] プログレスバー ]