授業資料/08 の変更点


#contents

* 前回の課題について [#u46c05d7]

** Fizz Buzz [#h24f3f45]
簡単な問題だが,苦労した学生もいるようなので Fizz Buzz についてもサンプルとその作り方を示しておこう.

*** 全体構造 [#kc4b0a07]
まずはいつものように全体構造からである.
// programu source 表記
#highlighter(language=ruby,number=on,cache=on){{
include Math

# Fizz Buzz を正しく返す関数
def fb(n)
  # 実装はこれから.とりあえず「プログラムが動くように」 n を出力しておく
  return n
end

# 以下,プログラム本体

# 変換された整数をいくつ数えるか
n_upper = ARGV[0].to_i

# 変換された整数の数
n = 0

# Fizz Buzz を試して行く.最初は 1 から.
i = 1

# n が指定された数になるまで i を増やしていく
while (n < n_upper) do
  # 画面出力
  print(fb(i),"\n")

  # 「Fizz Buzz の出力が単なる数字でない = 変換された」としてカウントを増やす
  if (fb(i) != i) then
    n += 1
  end

  # 次の数へ
  i += 1
end
}}

*** Fizz Buzz 関数の実装 [#h7ca79dc]
Fizz Buzz の性質からして,対象となる整数 ''n'' に対してプログラムのやるべきことは(素直には)
+ まず 15 の倍数かチェック (結果は Yes = ''Fizz Buzz'' を出力 or No) 
+ 上のチェックの結果が No ならば,(例えば) 3 の倍数かチェック (結果は Yes = ''Fizz'' を出力 or No)
+ 上のチェックの結果が No ならば,(例えば) 5 の倍数かチェック (結果は Yes = ''Buzz'' を出力 or No)
+ 上のチェックの結果が No ならば,整数 ''n'' そのものを出力

という順序になる.これは簡単な算数の問題だ.これを大学生が間違えると相当恥ずかしい…
&br;
CENTER:&size(24){''Fizz Buzz で間違えた学生は算数の能力について反省が必要かも''};
&br;

さて,話を戻して Fizz Buzz 関数の実装を見てみよう.
上のストーリーを素直に実装するならば例えば次のようになるだろう.
// programu source 表記
#highlighter(language=ruby,number=off,cache=on){{
def fb(n)
  if (n % 15 == 0) then
      return "Fizz Buzz"
    elsif (n % 3 == 0) then
      return "Fizz"
    elsif (n % 5 == 0) then
      return "Buzz"
    else 
      return n
  end
end
}}
迷うところなど何も無いぐらい簡単だ.もちろん,''elsif'' など使わなくても 次のように ''if'' 文を素直に重ねていっても良い.いずれにしても論理がわかっていればこ部分のプログラムはまったく難しくない.
// programu source 表記
#highlighter(language=ruby,number=off,cache=on){{
def fb(n)
  if (n % 15 == 0) then
    return "Fizz Buzz"
  else 
    if (n % 3 == 0) then
      return "Fizz"
    else 
      if (n % 5 == 0) then
        return "Buzz"
      else
        return n
      end
    end
  end
end
}}

出来なかった or 難しかったと思った人は,
&br;
CENTER:&size(24){''プログラミングする前に,論理をきちんと整理しているか?''};
&br;

と自分の行動を振り返ってみよう.もしも思い当たるふしがあるならば,キーボードに触る前に問題を整理してから取り組むようにしてみよう.

&br;
** Collatz 問題 [#h79bf52d]
まず,プログラムに課せられた条件を再掲しよう.

- 正整数 ''k'' に対して,Collatz の関数 ''f'' を何回適用すると 1 になるかを出力する関数 ''num_col(k)'' が ''def'' を使って定義されている.
- プログラム起動時にある程度大きな数 ''n'' (100ぐらい) が与えられる.
- 1 から始めて,''n'' まで ''num_col'' の結果を出力する.

ということだった.だからもちろん,
&ref(/materials/warning.png); ''num_col 関数がなければ失格''である.
もっときちんと言うと,総仕上げの問題については当然
&br;
CENTER:&size(24){''課せられた条件を満たさなければレポートとして受理されない''};
&br;

ので,適当に出せばいいやとおもっていると単位がつかなくて嘆くことになる.

*** 全体構造 [#e12635df]

さて,全体構造は単純に以下のようになるだろう.

// programu source 表記
#highlighter(language=ruby,number=on,cache=on){{
include Math

# Collatz 関数
def col(n)
  # 実装は後で.
  return n
end

# Collatz 関数を何回適用したら 1 になるかを返す関数. この関数を作れと指示されている.
def num_col(k)
  # 実装は後で
  return k
end

# 以下,program本体

# どこまで調べるか
n_upper = ARGV[0].to_i

# 1 から順に,n_upper まで num_col の結果を出力する
for i in 1..n_upper do
  print(i, ", ",num_col(i),"\n")
end
}}

&br;
*** 各関数の実装 [#m95ffbf1]

まず,定義通りで済む Collatz 関数を実装しよう.次のようになるだろう.
// programu source 表記
#highlighter(language=ruby,number=off,cache=on){{
def col(n)
  if (n % 2 == 0) then
      return (n/2)
    else
      return (3*n)+1
  end
end
}}

そして,Collatz 関数を何回適用したら 1 になるかを返す関数は while を使って例えば次のようになるだろう.
// programu source 表記
#highlighter(language=ruby,number=off,cache=on){{
def num_col(k)
  i = 0
  n = k

  while (n > 1) do # まだ 1 になってなければ Collatz 関数を適用する.
    n = col(n)
    i += 1
  end

  # ここへ来るのは,while を抜けた時なので,n=1 になっている.
  return i
end
}}

あとは,このプログラムを 100 ぐらいまで動かして,結果をグラフにしてグラフとプログラムの両方をレポートとして提出すれば良かった,という簡単な話だ.

* プログラム動作中にデータを貰う : gets 命令 [#rb9c304e]

プログラムの動作中に,キーボードやファイルからデータを貰いたいというシチュエーションがある.
こういう時に使う命令として ''gets'' (get string の略) という命令があるのでこれを学ぼう.

使い方は,おおざっぱにいって以下の通りだ.

** 1行に1個のデータを貰う場合 [#e97f9d05]

データを入力してもらうたびに,次のセットを行えば良い.
ただし,''a'' はデータを入れる変数名の「例」である.
&br;

&size(24){''&color(blue){a}; = gets''}; 
&size(24){''&color(blue){a};.chomp!''};
&br;

解説しよう.

最初の ''a=gets'' でキーボードから一行分の文字列入力が得られ,変数 ''a'' に代入される.
ただし,このままだと ''a'' の最後に「改行コード \n」という特殊な文字が入ったままであり,通常は困るだけなので,次の ''a.chomp!'' でその特殊な文字 ''\n'' を消去している.
&br;

CENTER:&size(24){''入力データにはいつもすぐ .chomp! しておこう''};
&br;

&ref(/materials/warning.png); ''gets = get string'' であることからわかるように,入力データは「文字列」として扱われる.数字として扱いたいときは変換が必要だ.
&br;

&ref(/materials/notes.png); 数字を二つもらってその平均を求める簡単なプログラムを組もう.ただし,入力を促す表示もするようにしよう.
&br;
この場合は例えば次のようになるだろう(ただし,似た様な繰り返しが出てきていてあまり良くないプログラムである).
// programu source 表記
#highlighter(language=ruby,number=on,cache=on){{
include Math

print("What is x? : ") # 入力を促す表示
x = gets
x.chomp!

print("What is y? : ") # 入力を促す表示
y = gets
y.chomp!

z = (x.to_f + y.to_f) / 2.0
# x, y に .to_f などをつけないと数字扱いにならないことに注意!

print("Average = ",z,"\n")
}}
例えばこのプログラムを実行すると,
>  What is x? : 1
>  What is y? : 2
>  Average = 1.5

などとなる.

** [応用] 1行に1個のデータを貰う場合. ただし,何行の入力があるかわからない場合 [#qa785e28]

例えば,「いくつかの」データの平均を知りたいが,データがいくつ有るか事前に分からないような場合がある.こうした場合ぐらい,入力に柔軟性を持たせたい.
そうした場合には ''while'' を使って次のようにするのがパターンだ.
&br;

&size(24){''while (&color(blue){a}; = gets) do''}; 
&nbsp;&nbsp;&nbsp;&nbsp;&size(24){''&color(blue){a};.chomp!''};
&nbsp;&nbsp;&nbsp;&nbsp;&size(18){''&color(blue){処理したいことをどんどん記述};''};
&nbsp;&nbsp;&nbsp;&nbsp;&size(18){''&color(blue){…};''};
&nbsp;&nbsp;&nbsp;&nbsp;&size(18){''&color(blue){ここまできたら,もう一度上に戻って「入力があれば」while の次の行に突入.};''};
&nbsp;&nbsp;&nbsp;&nbsp;&size(18){''&color(blue){「入力がなければ」下の end 以降に移る};''};
&size(24){''end''}; 
&br;

&ref(/materials/warning.png); (上級者向け) わかる人用に解説すると,入力がなければ ''gets'' の結果が空っぽになり,''a = get'' の代入に「失敗」する.そのため,while 文の条件が成り立たずに,end 以下へ制御が流れるという仕組みだ.
&br;

これは実例を見ないとピンとこない人もいるだろう.
&ref(/materials/notes.png); 数字を複数回入力してもらってその平均を求める簡単なプログラムを組もう.ただし,何回入力されるかは事前には分からないとしよう.
&br;
この場合は例えば次のようになるだろう.
// programu source 表記
#highlighter(language=ruby,number=on,cache=on){{
include Math

# データの個数
n = 0

# データの合計
sum = 0.0

print("? (to stop, push Ctrl+d): ") # 入力を促す表示.最初の一回分だけ余計に必要.
while (x = gets) do
  x.chomp!

  sum += x.to_f
  n += 1

  print("? (to stop, push Ctrl+d): ") # (次の)入力を促す表示
end

average = sum/n

print("\nAverage = ",average,"\n")
}}
例えばこのプログラムを実行すると,
>  ? (to stop, push Ctrl+d): 3
>  ? (to stop, push Ctrl+d): 2.1
>  ? (to stop, push Ctrl+d): 4.3
>  ? (to stop, push Ctrl+d):
>  Average = 3.13333333333333

(4回目の入力時に,''Ctrl'' キーと ''d'' キーを同時に押している)
などとなる.

&ref(/materials/warning.png); コンピュータの世界では,「入力は終りだよ」と応答したいときは ''Ctrl'' キーと ''d'' キーを同時に押す約束になっている.覚えておくとよいことがあるかもしれない.

** 1行に複数のデータを貰う場合 [#cd2106d5]

1行に複数のデータを書いてもらって入力という場合は,データを分割して配列の要素にしてしまうのがまあ使い易いやり方だろう.
データが空白で区切られて入力される場合,具体的には次のようになる.
&br;

&size(24){''&color(blue){a}; = gets''}; 
&size(24){''&color(blue){a};.chomp!''};
&size(24){''&color(blue){ary = a};.split()''};
&br;
この場合は,変数 ''ary'' が複数のデータを格納した配列となる.
よくわからないという人は,無理に理解せず,この「パターン」を暗記してしまっても良い.
&br;

&ref(/materials/warning.png); (上級者向け) ''.split()'' は,() の中に指定したもので区切られているとして文字列を分割して配列を返す操作である.何もいれないと空白区切りになる.他の場合については各自調べよ.
&br;

さらに,入力データが実は全部数字のつもりだという場合は,次の一行を加えて配列を変換すれば良い.
&br;

&size(24){''&color(blue){ary};.map!{|e| e.to_f }''};
&br;
よくわからないという人は,無理に理解せず,この「パターン」を暗記してしまっても良い.
&br;

&ref(/materials/warning.png); (上級者向け) ''.map!'' は配列の中身それぞれに同じ操作を加える命令である.
&br;

&ref(/materials/notes.png); さて,この機能を使って,数字を1行に複数個入力してもらってその平均を求める簡単なプログラムを組もう.ただし,何個入力されるかは事前には分からないとしよう. また,途中の変数の変換等がどうなっているのかを理解するために,途中の経緯も表示もするようにしよう.
&br;
この場合は例えば次のようになるだろう.
// programu source 表記
#highlighter(language=ruby,number=on,cache=on){{
include Math

# 入力を促す表示.
print("? : ") 

# まず普通に大きな文字列一つとして入力
x = gets
x.chomp!

# 現状の x を表示してみる
p x

# 空白で区切って配列に変換する
data = x.split()

# その配列を表示してみる
p data

# 配列の要素すべてを実数扱いに変換
data.map!{ |e| e.to_f }

# その配列を表示してみる
p data

# あとは平均を計算.
sum = 0.0
for i in 1..data.size do
  sum += data[i-1]
end
average = sum/data.size

# 平均を表示
print("Average = ",average,"\n")
}}
例えばこのプログラムを実行すると,
>  ? : 3 2.1 4.3
>  "3 2.1 4.3"
>  ["3", "2.1", "4.3"]
>  [3.0, 2.1, 4.3]
>  Average = 3.13333333333333

などとなる("3 2.1 4.3" と入力した場合).

** キーボードからではなく,ファイルからデータを入力する [#p3c77d02]

データが多い場合はキーボードから入力するのは面倒だ.また,既にファイルにデータが存在するならば,はやりそれを直接利用するのが賢い.
そこで,上の ''プログラムを変更せずに'',ファイルからデータを入力する方法を教えておこう.
&br;

例えば,''data.txt'' というファイルにキーボードで入力するのと同じデータが書いてあって,プログラム名が ''prg.rb''だとしたら,
&br;
&size(18){''&color(blue){ruby -w prg.rb }; < &color(blue){data.txt};''};
&br;
というように,
&br;
CENTER:&size(24){''プログラムの起動時に''};
CENTER:&size(24){''< データの入ったファイル名 を後ろにくっつける''};
&br;

と,そのファイルの中身がキーボード入力の代わりに使われる.

また,同様に
&br;
&size(18){''cat &color(blue){data.txt}; | &color(blue){ruby -w prg.rb };''};
&br;
というように,
&br;
CENTER:&size(24){''プログラムの起動時に''};
CENTER:&size(24){''cat データの入ったファイル名 | を前にくっつける''};
&br;

としても,そのファイルの中身がキーボード入力の代わりに使われる.

&br;
*** ''| tee'' と同時に使いたい [#q4c7cf6c]
ちなみに,以前学んだ ''| tee'' と同時に使いたい場合は,(出力ファイル名を ''output.dat'' と仮にして)それぞれ

&br;
&size(18){''&color(blue){ruby -w prg.rb }; < &color(blue){data.txt}; | tee &color(blue){output.dat};''};
&size(18){''cat &color(blue){data.txt}; | &color(blue){ruby -w prg.rb };  | tee &color(blue){output.dat};''};
&br;

とすればよい.
&br;

&ref(/materials/notes.png); 今までのキーボード入力を,すべてファイルからの入力に代えてうまくいくことを確かめてみよう.
&ref(/materials/warning.png); ファイルからの入力の場合,「データがもう終わり」というのは「ファイルが終わりに達した」ことで表される.

&br;
* 今日の総仕上げ [#y5f1ea23]

n x m 行列のデータを入力したら,その行列と転置行列をそれっぽく表示するプログラムを書こう.
&ref(/materials/warning.png); もちろん,任意のサイズの行列に対して動作しないといけない!
ただし,行列の形になっていないデータは想定しなくても良い.
なお,流れとしては ''gets'' を ''while'' の中で使うパターンになるはずだ.
&br;

具体的には実行すると
>  ? : 1 2 5 8
>  ? : 2 3 8 10
>  ? :
>  &br;
>  Input matrix =
>  [ 1.0 2.0 5.0 8.0 ]
>  [ 2.0 3.0 8.0 10.0 ]
>  &br;
>  Transposed matrix =
>  [ 1.0 2.0 ]
>  [ 2.0 3.0 ]
>  [ 5.0 8.0 ]
>  [ 8.0 10.0 ]

(1回目の入力で "1 2 5 8" と入力し,2回目の入力で "2 3 8 10" と入力し,3回目の入力で ''Ctrl'' キーと ''d'' キーを同時に押している)
というような動作をするプログラムを書け,ということになる.

&ref(/materials/warning.png); 以前に行列について書いたプログラムが参考になるはずだ.よくみてみよう.
&br;
&ref(/materials/notes.png); (上級者向け) n x n 行列 ''A'' のデータと n次元ベクトル ''b'' のデータを入力すると,連立一次方程式 ''Ax = b'' を解いて ''x'' を出力するプログラムが書けるだろうか.普通の消去法がわかっていればできるはずだが,さてさて.
&br;

* レポート [#u9c83f8c]

本日受けた講義および行った実習について,簡単にまとめた報告をせよ.
また,総仕上げの部分で自分で書いたプログラムを報告せよ.

もちろん各自の

+ 所属(学部,学科)
+ 学籍番号
+ 学年
+ 氏名
+ 日時
+ 肝心のレポート内容(得た知見,作業について気づいたこと等も)

を書くのを忘れないように. 

* about Icons, ClipArts [#u72e5ea4]
Some icons in this page are downloadable at [[ICONFINDER:http://www.iconfinder.net/]].

The "note" icon &ref(/materials/notes.png); designed by [[Marco Martin:http://www.notmart.org/]] is distributed with the LGPL licence,
the "warning" icon &ref(/materials/warning.png); designed by [[Alexandre Moore:http://nuovext.pwsp.net/]] with the GPL licence
and the "triangle" icon &ref(/materials/JNorth_arrow-right-sm.png); designed by [[Joseph North:http://sweetie.sublink.ca/]] is distributed with the [[Creative Commons (Attribution-Noncommercial-Share Alike 3.0 Unported):http://creativecommons.org/licenses/by-nc-sa/3.0/]] licence.

Some clip arts used in this page are downloadable at [[Open Clip Art Library:http://www.openclipart.org/]].
We deeply appreciate their superb works. With licence, they describe that "the actual clipart content on open clipart library is Public domain" in the web.

// ━┃┏┓┛┗┣┳┫┻╋


// コマンドライン入力は「行頭をブランクで始める」.
// コマンドライン出力は「行頭を > で始める」.

// 実習アイコン
// &ref(/materials/notes.png); 

// 注意アイコン
// &ref(/materials/warning.png);

// Link アイコン
// &ref(/materials/JNorth_arrow-right-sm.png);

// 大文字での強調 
// CENTER:&size(24){''ほげほげ''};

// programu source 表記
// #highlighter(language=ruby,number=on,cache=on){{}}