アルゴリズムとプログラミング2~擬似言語~

snack
snack

擬似言語とはアルゴリズムの理解を助けるなどの目的に使われる疑似的なプログラム言語のことっす。ここで勉強する疑似言語はIPAが試験用に作成したものになるっす。

ボキタロー
ボキタロー

ぼくはもう前回の勉強でへろへろなんですが・・・。

snack
snack

前回も言ったっすが、厳しい人はスルーしてもOKっす。余裕のある人だけ勉強すればいいっすからね。あまり深入りしすぎないように注意するっすよ。

擬似言語とは

前回はアルゴリズムを理解するために流れ図(フローチャート)を学習しましたが、実際にはプログラム言語を用いてアルゴリズムを記述することで、コンピュータ上でアルゴリズムを実行できるようになります。

プログラム言語にはいくつもの種類がありますが、学ぶべきプログラム言語は各個人の仕事や環境などによって異なるため、ITパスポート試験では特定のプログラム言語を用いた記述方法について問われることはありません。

ITパスポート試験ではプログラムの原理原則を学ぶために、流れ図を疑似的なプログラム言語(擬似言語)で表したものが出題されます。

疑似言語の記述形式

ITパスポート試験では、次のような形式で疑似言語が出題されます。

疑似言語

この表は試験中にも見ることができるため、覚える必要はありません。しかし、試験で初めてこの表を見てもおそらく理解できないと思いますし、そんな時間の余裕はないと思いますので、あらかじめどういう意味なのかを理解しておくことは必要です。

関数・変数の宣言

基本的に関数名や変数名は自由につけることができますが、いきなりプログラム中にこれを書いてもコンピューターはそれが関数や変数であるということが理解できません。そこで、まず「この文字列は関数・変数だよ」ということをコンピューターに教える必要があります。これを関数(変数)の宣言といいます。

疑似言語では、関数や変数を宣言する場合は次のように記述します。

記述例
〇fee(整数型:age)
 整数型:ret

feeには○が付いていることから関数、retは○が付いていないので変数、ということになります。

なお、関数名や変数名の前についている「整数型:」というのはデータ型(データの種類)を表しています。プログラム中でデータを扱う場合、そのデータがどんな種類のものなのかを最初に指定することで、プログラムが正しく変数などを扱うことができ、正確な演算や処理を可能にします。

このデータ型の種類には次のようなものがあります。

データ型意味
整数型整数(自然数に0と負の数を加えた数)を扱うためのデータ型0、123、-56
実数型実数(整数に小数を加えた数)を扱うためのデータ型3.14、-0.73
文字型1文字を扱うためのデータ型い、ぬ、ぼ、き
文字列型文字列(複数の文字)を扱うためのデータ型いぬぼき、こんにちは
論理型「真(true)」か「偽(false)」のどちらかを扱うためのデータ型true、false
主なデータ型(覚える必要はありません)

関数の呼び出し

最初に宣言した関数は、プログラム中で呼び出すことによって命令を実行することができます。なお、関数には引数(ひきすう)を持つものと持たないものが存在します。

引数を持たない関数

引数を持たない関数は、関数名の次に()を書きます。以下は”A”という文字を出力する関数の記述例です。

記述例
〇proc1()   //関数を宣言
 "A" を出力する   //処理内容

 proc1()   //関数の呼び出し
出力結果

”A”

「/* 注釈 */」や「 // 注釈」の注釈の部分はコンピューターでは実行されません。コードの説明やコメントなどのメモ書きのようなものだと思ってください。

引数を持つ関数

引数を持つ関数は、関数名の次に(型名:引数)を書きます。以下は引数を渡すとそれを3倍にするという関数の記述例です。

記述例
〇func(整数型:i)
 i×3

 func(5)
出力結果

15

i=5を引数として渡しているので、「5×3」の計算結果15が出力されます。

なお、関数は複数の引数を受け渡すことができます。その場合は引数をカンマで区切って指定します。以下は2つの引数(xとy)の平均値を返す関数の記述例です。

記述例
〇Average(整数型:x,整数型:y)
 (x+y)÷2

 Average(4,8)
出力結果

6

x=4、y=8を引数として渡しているので、「(4+8)÷2」の計算結果6が出力されます。

条件分岐に関する記述形式

if(条件式)~endif」と書くことで、もし条件式を満たせばその下に書かれた処理を実行します。以下は、「もしx≧yならば、xをansに代入し、最後にansの値を返す」というプログラムになります。returnは戻り値(関数の呼び出しによって得られる値)を意味します。

記述例
〇max(整数型:x,整数型:y)
整数型:ans

if (x≧y)
ans ← x
endif

return ans
参考

ITパスポート試験の[疑似言語の記述形式]の表では戻り値を持たない関数のことを手続と呼んでいますが、「関数」と「手続」の区別はあまり気にする必要ありません。どちらも同じようなものという認識でOKです。

条件式を満たさないときに別の処理を実行したい場合は、elseを使います。以下は、「もしx≧yならばxをansに代入し、そうでなければyをansに代入する。最後にansの値を返す」というプログラムになります。

記述例
〇max(整数型:x,整数型:y)
整数型:ans

if (x≧y)
ans ← x

else
ans ← y

endif

return ans

さらに、elseifを使うことで複数の条件を設定できます。以下は「もし条件式1を満たせば処理1を実行、もし(条件式1を満たさずに)条件式2を満たせば処理2を実行、それ以外(条件式1も条件式2も満たさない場合)は処理3を実行する」というプログラムになります。

記述例
if(条件式1)
処理1

elseif(条件式2)
処理2

else
処理3

endif

繰り返し処理に関する記述形式

繰り返し処理は次の3つがあります。

while文(前判定型)

前回は、1から5までの数字の合計を求めるプログラムを流れ図(フローチャート)で学習しましたが、これを疑似言語と比較すると次のようになります。

流れ図(フローチャート)と疑似言語

流れ図と擬似言語では、次の点が異なるので注意してください。

・流れ図と擬似言語では、代入の矢印の向きが異なる。

・繰り返しの条件について、流れ図では繰り返しが終了する条件が記載されるのに対して、疑似言語では繰り返しが行われる条件が記載される。

流れ図では「iが5よりも大きければ繰り返しを終了する」、疑似言語では「iが5以下の場合は繰り返す」と書きます。

do~while文(後判定型)

前判定型では繰り返しを行う条件を満たしているかどうかを判定したあとで処理が行われるのに対して、do~while文(後判定型)ではまず処理を行ってから、次に繰り返すかどうかの判定を行います。

記述例
do
処理

while(条件式)

for文

for文はforの後に制御記述を書き、その制御記述の内容に基づいて処理を繰り返すかどうかの判定を行います。以下は、1から5までの数字の合計を返すプログラムの記述例です。

記述例
整数型:i,ans

ans ← 0

for(iを1から5まで1ずつ増やす)
ans ← ans+i

endfor

return ans
出力結果

15

配列

配列とは、複数のデータを連続的に並べたデータ構造をいいます。”{” で配列の内容の始まりを表し、”}” で配列の内容の終わりを表します。各データを要素と呼び、要素は添字(インデックス)で識別されます。

配列

例えば、aという名前の配列の各要素の数値が13,10,8,15,3のとき、疑似言語では{13,10,8,15,3}と書きます。

一般的には配列の最初の要素は要素番号が0になりますが、ITパスポート試験では「配列の要素番号は1から始まる」と指定されている場合が多いので注意しましょう。

確認○×問題

問1

次のプログラムにおいて、手続 proc2 を呼び出すと、 “C”,“B”,“A”,“C” の順に出力される。

【プログラム】

〇proc1()

 ”A” を出力する

 proc3()

〇proc2()

 proc3()

 ”B” を出力する

 proc1()

〇proc3()

 ”C” を出力する

答え:〇

手続 proc2 を呼び出すと、まずproc3()が実行され、最初に“C” を出力し、2番目に“B”が出力されます。その次に、proc1()を実行します。

手続 proc1では、まず“A”を出力し、その次にproc3()が実行され、“C” を出力します。

以上より、出力順は”C”,”B”,”A”,”C”となります。

問2

次のプログラムを実行すると、”3,2″と出力される。

〔プログラム〕

整数型:x ← 1

整数型:y ← 2

整数型:z ← 3

x ← y

y ← z

z ← x

yの値とzの値をこの順にコンマ区切りで出力する

答え:〇

トレース表を作成して変数の値を確認します。

手順xyz
初期値123
①x ← y223
②y ← z233
③z ← x232

以上より、yの値とzの値をこの順にコンマ区切りで出力すると、”3,2″となります。

問3

ある施設の入場料は、0 歳から 3 歳までは 100 円、4 歳から 9 歳までは 300 円、10 歳以上は 500 円である。関数 fee は、年齢を表す 0 以上の整数を引数として受け取り、入場料を返す。プログラム中の( ? )に入る適切な字句は「age が 9 以下」である。

〔プログラム〕

〇整数型: fee(整数型: age)

 整数型: ret

 if (age が 3 以下)

  ret ← 100

 elseif ( ? )

  ret ← 300

 else

  ret ← 500

 endif

 return ret

答え:〇

if文による条件分岐で、年齢ごとの入場料を返すプログラムですが、プログラムを見ると、関数feeの結果として返される変数retが入場料を表しているということがわかります。

elseif (?)の処理では、ret(入場料)に300を代入していることから、(?)には「4 歳から 9 歳まで」という条件を入れる必要がありますが、ここで注意したいのは、if文は上から優先的に実行されるため、その上の「if~」の条件(つまり3歳以下という条件)は「elseif~」に当てはまる条件に含む必要がないということです。

要するに、3歳以下の場合は「ret←100」という処理が優先されるため、あえて「4歳以上でかつ9歳以下」と書く必要はないという意味です(もちろん書いてもいいですけど)。

したがって、(?)に入る適切な字句は「age が 9 以下」となります。

問4

関数checkDigitは、10進9桁の整数の各桁の数字が上位の桁から順に格納された整数型の配列originalDigitを引数として、次の手順で計算したチェックデジットを戻り値とする。プログラム中の[ ? ]に入る適切な字句は「j ← k+( j-10×k )」である。ここで、配列の要素番号は1から始まる。

〔手順〕
(1) 配列originalDigitの要素番号1〜9の要素の値を合計する。
(2) 合計した値が9より大きい場合は、合計した値を10進の整数で表現したときの各桁の数字を合計する。この操作を、合計した値が9以下になるまで繰り返す。
(3) (2)で得られた値をチェックデジットとする。

〔プログラム〕
〇整数型:checkDigit(整数型の配列:originalDigit)
 整数型:i, j, k
 j ← 0
 for(iを1からoriginalDigitの要素数まで1ずつ増やす)
  j ← j + originalDigit[i]
 endfor
 while(jが9より大きい)
  k ← j ÷ 10 の商 /*10進9桁の数の場合、jが2桁を超えることはない*/
  [ ? ]
 endwhile
 return j

答え:〇

まず、前半のfor文から説明していきます。「iを1からoriginalDigitの要素数まで1ずつ増やす」とありますが、iは配列originalDigitの添字(要素番号)になっているため、手順(1)の「配列originalDigitの要素番号1〜9の要素の値を合計する」を処理していることが分かります。

次のwhile文では、jが9より大きい場合に繰り返す処理であることから、手順(2)の処理を行っていることが分かります。つまり、「合計した値を10進の整数で表現したときの各桁の数字(10の位の数字と1の位の数字)を合計する」という処理を

k ← j ÷ 10 の商
[ ? ]

の2行で行っているということになります。

「k ← j ÷ 10 の商」は、10の位の数字をkに代入する処理となります。

例えば、9桁の数字の各桁の合計(j)が”58″という数字であった場合、「58÷10=5 余り8」なので商”5″は10の位の数字を表すことになります。

あとはこれに1の位の数字を足したいわけですが、どのように処理すればいいでしょうか?

9桁の数字の各桁の合計(j)が”58″の場合であれば、58(j)から50を引いてやれば1の位の数字”8″を表すことができます。

先ほど計算した10の位の数字k(= j ÷ 10 の商)を10倍したもの(k×10)が例で言うところの”50″に当たるので、これをjから引いてやれば1の位の数字を表すことができます。

最後に、変数jの10の位の数字と1の位の数字の合計をjに代入します(jが1桁になるまでこれを繰り返します)。

変数jの10の位の数字= j ÷ 10 の商=k

変数jの1の位の数字=j-k×10

変数jの10の位の数字と1の位の数字の合計= k+( j-10×k )

まとめると、[ ? ]に入るのは「j ← k+( j-10×k )」となります。

問5

関数calcXと関数calcYは、引数inDataを用いて計算を行い、その結果を戻り値とする。関数calcXをcalcX(1)として呼び出すと、関数calcXの変数numの値が、1 → 3 → 7 → 13 と変化し、戻り値は13となった。関数calcYをcalcY(1)として呼び出すと、関数calcYの変数numの値が、1 → 5 → 13 → 25 と変化し、戻り値は25となった。

プログラム中のa, bに入れる字句の適切な組合せは次のようになる。

ab
num + 2 × iiを2から6まで2ずつ増やす

答え:〇

[プログラム1]

関数calcXをcalcX(1)として呼び出し、変数 i が1→2→3と増えたとき、変数 num の値がどう変わるかをトレースしていきます。なお、初期値では「num ← inData」から、numに1が格納されます。

空欄aが「num+2×i」とすると、変数 num の値は次のように変わります。

i=1のとき:num=1+2×1=3

i=2のとき:num=3+2×2=7

i=3のとき:num=7+2×3=13

以上より、変数numの値が1 → 3 → 7 → 13 と変化するため、正しいことが分かります。

[プログラム2]

関数calcYをcalcY(1)として呼び出し、iを2から6まで2ずつ増やしたとき、変数 num の値がどう変わるかをトレースしていきます。なお、初期値では「num ← inData」から、numに1が格納されます。空欄aは「num+2×i」です。

i=2のとき:num=1+2×2=5

i=4のとき:num=5+2×4=13

i=6のとき:num=13+2×6=25

以上より、変数numの値が1 → 5 → 13 → 25 と変化するため、正しいことが分かります。

問6


関数 makeNewArray は、要素数2以上の整数型の配列を引数にとり、整数型の配列を返す関数である。関数 makeNewArray を makeNewArray({3,2,1,6,5,4})として呼び出したとき、戻り値の配列は{3,5,6,12,17,21}となる。ここで、配列の要素番号は1から始まる。

〔プログラム〕

◯整数型の配列:makeNewArray(整数型の配列: in)

 整数型の配列:out ← { } // 要素数0の配列

 整数型: i, tail

 outの末尾にin[1]の値を追加する

 for (iを2からinの要素数まで1ずつ増やす)

  tail ← out[outの要素数]

  outの末尾に(tail + in[i])の結果を追加する

 endfor

 return out

答え:〇

プログラムを順番に見ていきましょう。

①outの末尾にin[1]の値を追加する

配列inは{3,2,1,6,5,4}なので、in[1](1番目の要素)の値は”3″です。これを配列outの末尾に追加します。

最初outは要素数0の配列(空っぽの配列)なので、この時点でout={3になります。

②for (iを2からinの要素数まで1ずつ増やす)

inの要素数は6つあるので、iを2から6まで1ずつ増やしていきながら処理を繰り返します。

・i=2のとき

処理:tail ← out[outの要素数]

この時点でのoutの要素数は1つだけなので、out[1]=3となり、これをtailに代入します(tail=3)。

処理:outの末尾に(tail + in[i])の結果を追加する

tail=3、in[i]=in[2](配列inの2番目の要素)=2なので、

outの末尾に”5″(=3+2)を追加します。

この時点で、out={3,5になります。

・i=3のとき

処理:tail ← out[outの要素数]

この時点でのoutの要素数は2つなので、out[2]=5となり、これをtailに代入します(tail=5)。

処理:outの末尾に(tail + in[i])の結果を追加する

tail=5、in[i]=in[3](配列inの3番目の要素)=1なので、

outの末尾に”6″(=5+1)を追加します。

この時点で、out={3,5,6になります。

・i=4のとき

処理:tail ← out[outの要素数]

この時点でのoutの要素数は3つなので、out[3]=6となり、これをtailに代入します(tail=6)。

ここまで来ると皆さんお気づきの通り、tailは配列outの末尾の要素の値を格納するための変数です。

処理:outの末尾に(tail + in[i])の結果を追加する

tail=6、in[i]=in[4](配列inの4番目の要素)=6なので、

outの末尾に”12″(=6+6)を追加します。

この時点で、out={3,5,6,12になります。

・i=5のとき

処理:tail ← out[outの要素数]

この時点でのoutの要素数は4つなので、out[4]=12となり、これをtailに代入します(tail=12)。

処理:outの末尾に(tail + in[i])の結果を追加する

tail=12、in[i]=in[5](配列inの5番目の要素)=5なので、

outの末尾に”17″(=12+5)を追加します。

この時点で、out={3,5,6,12,17になります。

・i=6のとき

処理:tail ← out[outの要素数]

この時点でのoutの要素数は5つなので、out[5]=17となり、これをtailに代入します(tail=17)。

処理:outの末尾に(tail + in[i])の結果を追加する

tail=17、in[i]=in[6](配列inの6番目の要素)=4なので、

outの末尾に”21″(=17+4)を追加します。

この時点で、out={3,5,6,12,17,21になります。

以上より、戻り値の配列は{3,5,6,12,17,21}となります。