情報処理1 解説サイト

関数

今回は関数のおさらいと課題を解くにあたっての考え方について簡単に解説したいと思います.

プログラムは関数が組み合わさって構成されています.今まで作成したプログラムには

int main(void){
        ...
}

という部分があったと思います.これもmain関数と呼ばれる関数の1つです.

関数は自分で定義することができ,ソースコードの中で何度も利用することができます.そのため,同じ処理を何度も記述する必要がなくなり,プログラムが簡潔になります.プログラム作成にかかる時間が短縮でき,バグの削減にもつながります.また,処理を関数として一度作成しておけば,他のソースコードで同様の処理を行いたい場合に,その関数を適用することもできるため,正しい関数をたくさん定義しておくことは非常に有益です.

  • 関数の定義

関数の作り方を復習します.下に関数の書き方とその具体例を示します.

戻り値の型  関数名(仮引数の型  仮引数名1, 仮引数の型  仮引数名2)
{
    処理 ...

    return 戻り値;
}
int add_number(int number1, int number2)
{
    int sum;              //変数を定義
    sum = number1 + number2;    //number1とnumber2の和をsumに代入

    return sum;
}

まず,戻り値の型,関数名,仮引数の型,仮引数名を記述します.ここでは,戻り値sumの型はint型であるのでintとします.関数名はその関数がどのような処理を行っているのかわかる名前にすると良いでしょう.仮引数の型と個数は,関数の呼び出し元の実引数の型と個数に合わせなければなりません.

引数の個数はいくつでも良く,複数の場合はカンマで区切ります.戻り値の個数は0か1です.

void 関数名(void)
{
    処理...
}

戻り値や引数がない関数は上のようにvoidと記述します.

  • 関数の呼び出し

関数の使い方を復習します.下に具体例を示します.

#include <stdio.h>

void print_sentence(void)
{
    printf("りんごとみかんがあります.\n");
}

void print_number(int number1, int number2)
{
    printf("りんごは %d 個です.\n", number1);
    printf("みかんは %d 個です.\n", number2);
}

int add_number(int number3, int number4)
{
    int sum;              //変数を定義
    sum = number3 + number4;    //number3とnumber4の和をsumに代入

    return sum;
}

int main(void)
{
    int apple = 10;
    int orange = 25;
    int total;

    print_sentence();
    print_number(apple, orange);
    total = add_number(apple, orange);

    printf("合計%d\n", total);

    return 0;
}
  1. プログラムが起動され,main関数が実行されます.
  2. 変数が定義されます.
  3. print_sentence( )で関数を呼び出します.引数がないので( )の中は空にします.プログラムの処理はprint_sentence関数へと移り,関数の中の処理が実行されます.末尾の}に到達すると,その関数を抜けてmain関数に戻ります.
  4. print_number(apple, orange)で関数を呼び出します.プログラムの処理はprint_number関数へと移り,関数の中の処理が実行されます.このときnumber1number2に,それぞれappleorangeの値の1025が代入され,処理が実行されます.末尾の}に到達すると,その関数を抜けてmain関数に戻ります.
  5. add_number(apple, orange)で関数を呼び出します.プログラムの処理はadd_number関数へと移り,関数の中の処理が実行されます.このときnumber1とnumber2に,それぞれappleとorangeの値の10と25が代入されます.そして,関数の中の処理が実行され,return文に到達すると,その関数を抜けてmain関数に戻ります.このとき,戻り値sumtotalに代入されます.
  6. totalが出力されます.


以上が,授業のおさらいです.しっかりと復習をしておきましょう.
では,課題の考え方について解説したいと思います.

  • 5組

1. 自然数を入力すると,1からその数値までの和を求め表示するプログラムを作成せよ.

5回の授業の内容が理解できていれば,簡単に解けると思います.for文やwhile文を用いて合計値を計算しましょう.また,0以下の数字が入力された場合はエラーと表示するようにプログラムを分岐させましょう.条件分岐は第4回の授業で習いました.
練習問題Q1を参考にすると良いと思います.

2. 縦横の長さを整数で入力すると,#で長方形を表示するプログラムを作れ.

2重ループを用いて#を表示させます.適切に改行して長方形を作成しましょう.また,負の値が入力された場合はエラーと表示するようにプログラムを分岐させましょう.
練習問題Q4を参考にすると良いと思います.

3. 月と月始めの曜日を入力すると,カレンダーを表示するプログラムを作成せよ.

第4回の練習問題Q3で何月が何日まであるかは分岐させることができるようになったと思います.この問題では1から28or30or31までをカレンダーのように配置するところがポイントです.
このプログラムを作成する方法の1つとして2重ループを用いることが考えられます.第5回の練習問題Q4を参考にすると良いと思います.日月火...土は最初にプリント文で表示させ,その下に6行7列の表を作りましょう.数字だけでなく適切に空白も表示することで何曜日始まりでも対応できるプログラムが作成できます.最後の日まで表示できたらbreak文でループから脱出する,もしくは空白を表示します.

...
printf(" 日 月 火 水 木 金 土\n");

for(int i=0;i<=6;i++){
    for(int j=0;j<=7;j++){
        if(???? > day){
            break;
        }
        else if( ... ){
            ...

4. 入力した自然数以下の素数をすべて表示するプログラムを作成せよ.

この問題のポイントは素数の判定です.
素数とは1と自分自身以外に正の約数を持たない自然数で,1でない数のことです.そこでまず,2から入力された自然数までを表示するプログラムを作成しましょう.これは練習問題Q3を参考にすると良いと思います.それができたら,素数であるかどうかの判定をして,素数であれば表示するようにプログラムを追加しましょう.また1より小さい数字が入力された場合はエラーと表示するようにプログラムを分岐させましょう.
判定の一例として,下に示すようなフラグを用いた処理が考えられます.フラグの有無を条件にして表示するかしないかの分岐を行います.そして,ループの最後でフラグを戻し,次の数字の判定を同様に行います.このようにすれば素数だけを表示することができると思います.
他にもいろいろな方法があると思うので,試行錯誤してみましょう.プログラミングは書けば書くほど,考えれば考えるほど力がつきます.
繰り返しになりますが,素数とは1と自分自身に約数を持ちません.つまり,素数は自分より小さな数字で割られた時,剰余が0になることはありません.逆に,素数でない数字は剰余が0になることがあるということです.

int main(void){
    int flag = 0;
    ...
    for( ... ){    //2から入力された自然数まですべての数を順番に調べていく
        ...
        if( ... ){    //素数の判定.  もしその数が...なら
            flag = 1;    //フラグをたてる
        }
        ...
        if(flag == ??){    //もしフラグが...なら
            printf("%d", ??);関数
        }
        flag = 0;
    }
    ...
}

5. 縦,横,斜めのいずれの方向においても,3数の和が全て等しくなるような値をプログラムにより求めよ.手計算でなく,必ずプログラムで解答を求めること.

数独とは若干異なる問題ですね.TAが考えた解法の1例を示します.下図のように左下のマス(”3行1列目の成分”と言います.詳しくは線形代数を勉強して下さい.)をxとおくと下図のように相対的関係から文字式が求まるマスがあります.

-----------------------
|   3   |      |      |  
-----------------------
|   4   |  5   |  x-2 |
-----------------------
|   x   |      |  x-1 |
-----------------------

ここまで求められたら実はxは一意に定まるので,この問題はただひとつの解にたどり着きます.この手法でワタクシTAがプログラムでこの問題を解くとすれば,和の組み合わせ方は8通りあるので,まずその8通りの組み合わせ方のうち,2つのマスに数字が含まれているものをfor文またはwhile文の中でif, else文を用いて探索します.見つけた組み合わせ方は何かしらの方法でリストとして記録して置きます.(今回は3通りと定まっていることは既知としても良いでしょう笑)そして,最初に見つけた組み合わせ方の未知数をxと宣言し,残り2つの組み合わせ方の未知数を差分から文字式として求めます.あとは,文字式としても求められていないマスの中が組み合わせとして重複しているマスと一致している場合を探索し,そこからxをfor文で-n~+nまで力技で方程式として成り立つxを求めます.これを残りの未知のマスを繰り返し探索して行います.この提案手法は手計算でやる手順を具現化しているつもりですが,かなり言葉足らずかと思われます.ご容赦下さい.(しかも皆さんの知らないコーディング技術も使わないと,かなり気合と根性が必要になってくるかと思われます.)

しかし,上記の3, 4, 5, x, x-1, x-2をプログラム上において既知としてしまえば,以下のようにもっとCPらしくゴリ押し計算で簡単に求めることもできます!

for(int n=-99999;n<99999;n++){
      if(8通りすべての組み合わせ方の和が等しい){
          ...
      }
}

他にもっと良い手法もあるかもしれませんね!


  • 6組

1.以下のサンプルコードのように,しきい値60以上であれば合格, それ未満であれば不合格となるように判定する関数 eval() を作って下さい.

まず、今回作成するeval()関数はmain文でどのように使われているでしょうか

    if(eval(test_result)){
        printf("テストの結果は「合格」です.\n");
    }else{
        printf("テストの結果は「不合格」です.\n");
    }   

if文の条件式で使われていますね、
if文はif(条件式)で条件式が正しければ真、間違っていれば偽を返す関数でした。

     int a=0;
     if(a==0){  
          printf("aは0と等しい\n");
     }else{
          printf("aは0と等しくない\n");
     }

このような使い方をすると条件式は成り立っているので「aは0と等しい」と表示されます。では、次の場合はどうでしょうか

     int a=0;
     if(a=0){  
          printf("aは0と等しい\n");
     }else{
          printf("aは0と等しくない\n");
     }

条件式がa==0からa=0に変わりました。この条件式でもコンパイルエラーにはならずにプログラムは作動します。この場合も「aは0と等しい」と表示されるような気がしますね、
ですが実はこの場合は条件式は偽の判定になり、「aは0と等しくない」と表示されます。これはC言語において
≠0 : 真
=0 : 偽
という決まりがあるためです。つまりif(0)は偽の判定、if(0以外)は真の判定になります。

以上のことを踏まえて問題に戻ると、eval()関数は引数の値がTHRESHOLD(=60)以上なら0以外の数(例えば1)、それ以下なら0を返す関数にすれば良いということになります。




2.直角三角形の短辺2つの長さ(a,b)を入力すると,長辺の長さ(c)を返す関数を作りましょう. a,bの値はそれぞれキーボードから入力します

三平方の定理を使いましょう.ルートの計算はmath.hをインクルードしてsqrt()関数を使います。





3.QUIZの5番目の問題は,1から3までの3個の2乗和を求めました. このプログラムを改良して,1からNまでの2乗和を求めるプログラムにしてください. このとき,Nの値は,scanf()関数でキーボードから与えられるようにしてください.

入力した数までの2乗和を足し続けるプログラムです.

int function(...){
    int sum;
    ...
    for( ... ){ 
                        //2乗和の計算を行う
    }
    ...
              return sum;
}

for文で足し続ける時に int sumという数を保存するようにして 戻り値にsum を取って表示するようにしてください.




4.速度0で静止しているロケットに点火してロケットを打ち上げます.ロケットの質量をm,推力が F で一定だとしたとき, 打ち上げから時間t秒後の速度v(t)を求める関数を作って下さい.引数はm,F,tで,戻り値がv(t)です.


空気抵抗を考慮する必要ないです.速度を求める公式 V = at + v0 を用いて関数を作ってください.また,加速度を求めるのは 運動方程式を利用して求めてください.

5.関数f(x)=sin(x^2)の数値積分を実行するプログラムを作成してください. なお,積分は最も単純な長方形近似で計算して良いです.
基本的な積分がわかっていれば特に困らないと思います。わからない時は積分の復習をしましょう。
問題3を参考にしてdx * function()をsumに足していきましょう


|