今回の学習項目です。
次の実行結果に示されるように、2つの数をキーボードから入力し、 その小さい方の数と、大きい方の数を順に表示するプログラムを作成しなさい。 (オレンジ色の文字はキーボードからの入力を表しています。 キーボードから数の入力を受け取る方法は、 ここを参照してください。)
2つの数を入力してください。 1番目:30 2番目:10 10 30
次の実行結果に示されるように、3つの数をキーボードから入力し、 それらを小さい方の数から大きい方の数へ、順に表示するプログラムを作成しなさい。 (オレンジ色の文字はキーボードからの入力を表しています。 キーボードから数の入力を受け取る方法は、 ここを参照してください。)
2つの数を入力してください。 1番目:30 2番目:10 3番目:20 10 20 30
今日は配列について学びます。
配列はプログラミングには欠かせない最も重要な基本要素、
基本的な「データ構造」の一つです。
配列をマスターしない限り、現実のプログラミングはできないと言っても言い過ぎではありません。
繰り返しがコンピューターの力のもとだといいましたが、配列と一緒に使うことで、
コンピューターはますます強力になると言ってもよいでしょう。
注意:「アルゴリズムとデータ構造」では配列だけではなくいろいろなデータ構造を学びます。それによりますます強力なプログラムが書けるようになります。
それでは、配列とはどんなものでしょうか。たくさんのものをしまったり出したりできる、便利な入れ物です。ここまでは変数と同じように、入れ物として働いているだけですが、変数と違って、一つだけではなく、たくさんのものを入れたり出したりして処理することができます。 変数の集まりであると考えることもできます。 変数に値を入れることができるということは、変数がいろいろな値を取れるということになります。 (変数という言葉の由来です。)配列は複数の変数の集まりと見ることができ、したがって、複数の値を扱うことができるのです。 変数は変数名によって保持している値を扱いますが、 配列では、次に述べるような形で、名前で直接値を扱うのではなく、指標(インデックス)といわれる数によって、 値を扱うことができるのです。これが変数をとても便利に使える大きな理由の一つでしょう。 このようなことが何を意味するのか、下の例で一歩、一歩見て行きましょう。
配列の特徴を複数の変数を使う場合と対比してしてみましょう。いつくもの名前と挨拶を扱うプログラムを、配列を使わないで、変数だけで作ってみましょう。 まず、用意した nameA,nameB,nameC,nameD,nameEという5個の変数に、 名前を表す文字列を代入しておきます。それを次々に出力していくことにします。
nameA = "太郎" nameB = "紘美" nameC = "克規" nameD = "倫哉" nameE = "隆弘" #ここまでで、変数への名前の代入ができました。 #これから後は出力です。 print "こんにちは、", nameA, "さん\n" print "こんにちは、", nameB, "さん\n" print "こんにちは、", nameC, "さん\n" print "こんにちは、", nameD, "さん\n" print "こんにちは、", nameE, "さん\n"このプログラムを実際に、実行してみてください。
今度は、変数ではなく、配列の中に、上の5個の名前を入れます。 まず、nameという変数を用意します。準備として、その中に、空の配列とを入れておきます。 これは、次のような代入文でできます。
name = [ ] #変数nameに空の配列をまず用意しておく。配列は、Rubyだけでなく、多くの言語で[ ]を使って表すことが普通です。 次が肝心なところですが、以下のようにすることで、 名前を表す文字列を順番に配列の中に入れて行くことができます。
name[0] = "太郎" #配列の0番目の要素に"太郎"という文字列を代入する。 name[1] = "紘美" #配列の1番目の要素に"紘美"という文字列を代入する。 name[2] = "秀樹" #以下同様。 name[3] = "倫哉" name[4] = "隆弘"この例は、変数nameの中の配列の0番目から4番目までのところにそれぞれ名前を代入しています。 数を使って区別していることが重要です。この数のことを 配列の指標(インデックス)と言います。 ここで指標は0以上の整数であることに注意してください。 これは、配列が0番から始まるということであり、 「配列の一番最初の要素の指標が0」ということです。 そして、ここでの命令はみな
変数名[指標] = 値という形をしています。 ここで、
print name[2], "\n"を実行すると、どんな出力結果が出てくるでしょう?
秀樹が出てきます。name[2]に秀樹を代入してあるので、当然ですね。
以下では、nameという変数の中の配列には名前を順番に入れておきます。 プログラムを実行すると、何が出力されるのか予測して報告しなさい。 そのあと、実際に実行して、予測と合ったかどうか報告しなさい。 予測と合わなかったら、その理由も報告すること。
name = [] name[0] = "太郎" name[1] = "紘美" name[2] = "秀樹" name[3] = "倫哉" name[4] = "隆弘" i = 3 print name[i], "\n"
配列がプログラミングの強力な道具になるのは、数(指標)によって配列の内容を取り出せるからです。 forやwhileの繰り返し文と組み合わせて使うことで配列は特に便利になります。 次の課題を行うことで、そのことを見てください。
次のプログラムを一歩一歩追いかけて、実行すると、何が出力されるのか予測して報告しなさい。 そのあと、実際に実行して、予測と合ったかどうか報告しなさい。 予測と合わなかったら、その理由も報告すること。
name = [] name[0] = "太郎" name[1] = "紘美" name[2] = "秀樹" name[3] = "倫哉" name[4] = "隆弘" i = 0 while (i < 5) print "名前=", name[i], "\n" i = i + 1 end
前のプログラムと比べるとこのプログラムでは、 print文を5回書かずにたった一回だけ書けばよいことになり、 プログラムが簡潔になっています。配列の中の要素の数がどんなに大きくなっても、 whileで書かれているプログラムの中心部分は同じです。
実際に、配列の要素を増やしてみましょう。 配列にたくさんのものを一度に入れたいときは、次のように一行に書くこともできます。 要素同士は(半角文字の)コンマ , で区切られていることに注意してください。
nameData = ["太郎","紘美","秀樹","倫哉","隆弘","秀樹","花子","秀美","英俊","正樹","一彦","泉"] i = 0 while (i < 12) print "名前=", nameData[i], "\n" i = i + 1 endこのようにwhileを使えば、配列がどんなに大きくなっても、 プログラムを変えずに配列の内容を書き出すことができます。
さらに、配列に次に上げる.sizeを使うともっとやりやすくなります。 .sizeというのは、配列の要素の数を返せという命令です。 次のように、配列を表す変数もしくは配列そのものとsizeをピリオドで結んで使います。
ary = ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"] print ary.size, "\n" print ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"].size,"\n"このプログラムを実行すると、11が表示されます。この11は配列の要素の数です。 このように配列の要素の数を自分で数えなくても計算機が答えてくれます。 この.sizeを使って、前のプログラムを書き換えてみましょう。
nameData = ["太郎","紘美","秀樹","倫哉","隆弘","一彦","花子","秀美","英俊","正樹","一彦"] i = 0 while (i < nameData.size) print "名前=", nameData[i], "\n" i = i + 1 end
先のプログラムと違うところは、 nameData.sizeを使っているところだけです。 nameDataという変数と"."でつないでいるところに注意してください。 変数の中の配列の要素の数(サイズ)を値として得ることができます。 (ここでは11です。) ですから、上のプログラムのwhileは配列の要素の数だけ、繰り返すのです。
こんにちは、太郎さん こんにちは、紘美さん こんにちは、秀樹さん こんにちは、倫哉さん こんにちは、隆弘さん
配列には、文字列に限らず何でも、入れることができます。 (変数に何でも入るのと同じです。)数を扱ってみましょう。
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9]
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] print data.size,"\n" print data[0], "\n" print data[4], "\n" i = 5 print data[i], "\n" print data[i+3], "\n"
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9]
2 4 7 3 #(中略) 30 9
iなどの変数を使って、配列の指標と繰り返しの指標を連動させることがこつです。
典型的なパターンです。
... while (i < data.size) ... .. data[i] ... end
data = [2,4,7,3,9,6,4,4,10,23,1,1,1,7,8,6,25,30,30,9] i = 0 sum = 0 while ( i < data.size) sum = data[i] i = i + 1 end print "配列の合計は", sum, "です。\n"
出力結果は次のようになります。
配列の合計は190です。
配列はfor文を使うと、簡単に扱えることがあります。
次のような形で扱います。
data = [e0, e1, ... , en] for ele in data 文1 文2 ... end
for文を理解する上でのポイントは、inの前の変数です。代入文が明示されていませんが、 配列の中からその要素が一つ一つ取り出されては、eleに代入され、実行されることを繰り返します。
何回繰り返し実行されるのかという回数は、配列の要素の数と同じになるのは当然です。whileを使った場合と比べてforを使う繰り返しは、指標(インデックス)を使わずに配列の要素を直接扱えるという特徴があります。
課題1-3で作成したプログラムをfor文を使って書き直しなさい。
課題1-6で作成したプログラムをfor文を使って書き直しなさい。
配列の中から必要なものだけを取り出して、処理する形はよくあるプログラムの「パターン」です。 このパターンに慣れておく必要があります。if文とうまく組み合わせることがこつです。
数の大小の判定などは、次の式でできます。a == b | aとbは等しい |
a != b | aとbは等しくない |
a < b | aはbより小さい |
a > b | aはbより大きい |
a <= b | aはb以下 |
a >= b | aはb以上 |
また、一つの条件式で書けない複雑な条件式(真偽の値の合成)は、 論理演算子と呼ばれるものを使って、条件を組み合わせて表現します。
「かつ」(and)は && で表現され、「または」(or)は || で表現できます。 これらは条件と条件の間に書きます。 また、「でない」(否定, not)は ! で表し、条件の前に書きます。
A && B | AかつB |
A || B | AまたはB |
! A | Aでない |
例えば、「numが3以上でかつ10以下」という条件式は、次のようになります。
(num >= 3 && num <= 10)
また、「numが3または10」という条件式は、次のようになります。
(num == 3 || num == 10)
さらに、『「nが3以上で mが10以下」でない』という条件式は、次のようにかけます。
! (n >= 3 && m <= 10)
次の配列の中から、10以上、20以下の数をとり出し、表示するプログラムを作成しなさい。
numbers = [4,12,45,21,18,7,20,5,51,16,10] #この後の部分を作る
出力は以下のようになる。
12 18 20 16 10
上の課題で、今度は10以上、20以下の数をとり出すのではなく、取り出した数だけ合計し結果を出力しなさい。
出力は以下のようになる。
10以上、20以下の数の合計は76です。
上の課題で、今度は10以上、20以下の数の合計ではなく、そのような数が幾つあったのかを数え、その数を出力しなさい。
出力は以下のようになる。
10以上、20以下の数は全部で5個あります。
以下のnekodataという変数の中の配列にはたくさんの「ねこ」が入っています。この中から、プログラムを作って、"とらねこ"を探し出して、何匹いるのか、その数を報告してください。以下のnekodataという変数に配列を代入する部分をプログラムの頭にコピーして使ってください。
nekodata = ["しろねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "とらねこ", "しろねこ", "とらねこ", "とらねこ", "くろねこ", "みけねこ", "しろねこ", "とらねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "とらねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "とらねこ", "みけねこ", "とらねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "とらねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "ばけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "とらねこ", "しろねこ", "しろねこ", "みけねこ", "くろねこ", "うみねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "くろねこ", "みけねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "とらねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "とらねこ", "しろねこ", "くろねこ", "みけねこ", "くろねこ", "うみねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "しろねこ", "しろねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "みけねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ", "とらねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "くろねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "みけねこ", "くろねこ", "しろねこ", "みけねこ", "やまねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "くろねこ", "くろねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "みけねこ", "とらねこ", "とらねこ", "くろねこ", "しろねこ", "しろねこ", "みけねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "くろねこ", "みけねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "しろねこ", "しろねこ", "しろねこ", "しろねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "あかねこ", "くろねこ", "みけねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "しろねこ", "くろねこ", "しろねこ", "くろねこ", "くろねこ", "くろねこ", "みけねこ", "しろねこ", "みけねこ", "みけねこ", "みけねこ", "しろねこ"]
変数の中の文字列が同じかどうかなども、数と同様に==を使って判定できます。
if(neko == "くろねこ") ... end
プログラムの中で文字列を扱うには、"..."(ダブルクオート)で囲むことを忘れずに。
while (条件判定式) ... ... if (条件判定式) break else ... end ... end
回数を限定しないで繰り返すためには、
while (true) ... ... ... end
というような形が使えます。条件判定式の中が常にtrue(真)なので、終わりません。
次のような形も使えます。while(true)の代わりに使えます。
loop do ... ... ... end
次のように、breakを使えばよい。
while (...) ... input = gets.to_i if (input == 0) break else print "... それではもう一回やりましょう。\n" sleep(2) end ... end
課題1-12のnekodataの中に"あかねこ"があれば
"みつけた!"と表示してbreakを使って繰り返しをぬけ、
なかった場合は"いなかった"と表示するプログラムを作成しなさい。
この結果はどうなるでしょう。
課題1-13と同様に、nekodataの中に今度は"ぶちねこ"があれば
"みつけた!"と表示してbreakを使って繰り返しをぬけ、
なかった場合は"いなかった"と表示するプログラムを作成しなさい。
この結果はどうなるでしょう。
Rubyにはprintの他にもディスプレイに文字を出力するための命令文がいくつか用意されています。 今回はそのうちの一つのpについて説明します。
pというのは、出力用ですが、主にプログラム開発中に用いるprint文です。つまり、プログラムを作成する過程で誤りをみつけたり、
途中の段階まで正しく動作していることを確かめたりするときに使います。
(完成したプログラムには使わないようにしてください。)
では実際にprintと
どのような違いがあるのか、比較して見てみましょう。まずはprintの場合です。
print "123\n" print 123
123 123
printを使うと、文字列の "123\n" も、数の 123 も、同じように 123 と表示されます。
今度はpを使った場合です。
p "123\n" p 123
"123\n" 123
pを使うと、文字列は "123\n" 、数は 123 というように、プログラム中での表現と同じ形で出力されます。 改行コード("\n")も無効になり、そのままの形で出力されます。 実行結果が改行されているのは、pには自動的に改行を入れる働きがあるからです。
最後にpを使って配列を出力した結果を見てみましょう。
p [1, 2, 3] ary = [4, 5, 6] p ary
[1, 2, 3] [4, 5, 6]
配列の場合も、やはりそのままの形で出力されます。変数に代入した場合も、printと同じように 変数から値を取り出し、取り出した値をそのままの形で出力してくれます。
このように、pには変数の持つ値をわかりやすい形で出力してくれる働きがあります。 プログラムが複雑になればなるほど、実行過程の変数の値を正確に把握するのが困難になります。 しかし、それがわからなければ正しいプログラムを書くことはできません。 そんなときはこのpを使って途中経過を確認しておくことが必要になります。
pを使って全角文字(2バイト文字)を表示するときは、 ruby ではなく ruby -Ks としてrubyを起動するか、 もしくはプログラムのファイルの1行目に $KCODE = "s" と書いておいてください。 これを書かないと、全角文字をpで表示したときに文字化けしてしまいます。
p "文字化けします。"
"\225\266\216\232\211\273\202\257\202\265\202\334\202\267\201B"
$KCODE = "s" p "こちらは文字化けしません。"
"こちらは文字化けしません。"
実行時にKsというオプションを付けた場合は以下のとおりです。
H:>ruby -Ks program.rb
ここでは配列を使った、今までよりも少し難しい問題を扱います。 この問題を解くことによって、どのようにプログラムを組んだらよいか、 問題を解くためにこのプログラムではどういうことをどういう順番でさせていったらよいか、つまり、アルゴリズム を考える必要性が分かることでしょう。
数を要素とする配列の中から最大の要素を表示するプログラムを作成しなさい。 例えば
data = [2,4,27,3,19,26,24,34,10,23,1,11,21,7,8,6,25,35,30,9]に対しては35が表示されます。 [ヒント:最大の要素を求めるアルゴリズム]
このプログラムには、配列を記憶する変数以外に、(途中までの)最大値を記憶するための変数が必要になります。それを仮に x とします。
まず最初に、xに配列の最初の要素をいれておきます。
そして、配列の要素をひとつずつxと比較し、それがxよりも大きければxの値をその値にします(更新する、という)。
配列の要素をすべて調べた後のxの値が求める最大の要素です。
課題2-1とは逆に、最小の要素を表示するプログラムを作成しなさい。 課題2-1のdataの値では、1が表示されます。
数を要素とする配列の中から2番目に大きな要素を表示するプログラムを作成しなさい。 例えば
data = [2,4,27,3,19,26,24,34,10,23,1,11,21,7,8,6,25,35,30,9]に対しては34が表示されます。ここで、dataの値が[1,2,3,3]のように最大値が2つある配列の場合は、その中で2番目に大きな数、この例では最大値でもある3を返すものとします。
課題2-1では(途中までの)最大値を記憶するための変数 x が必要でした。 この課題では、それ「以外」に、2番目に大きな要素を記憶する変数が必要です。 それを仮に y とします。 まず、配列の先頭と2番目の要素の大きい方をx、小さい方を yとします。 そして、それら以外の配列の要素をひとつずつxおよびyと比較し、xとyの値を更新します。(ここでどういう比較をどういう順番で行えば効率がよくなるか、よく考えてください)。 配列の要素をすべて調べた後のyの値が求める2番目大きな要素です。