[プログラミング]なぜ関数を使うのか ~動機やメリットは?~

スポンサーリンク

対象読者

  • プログラミング初心者の方
  • または経験者の方

結論

関数(function)は、プログラミング言語の文法の初級で登場する。基本中の基本だ。

関数とは、複数の処理をまとめたものだ。なんて単純明快なんだ!使い方も簡単だ。しかし、関数を有効に使いこなせていないプログラマは少なくない(当社比)。

なぜなのか。それは、文法を知っているだけで、関数を使う動機やメリットを理解していないからだ。文法の知識だけではダメだし、経験だけあってもダメだ。

そこで、本記事は文法の背景にある考え方を示し、関数を適切に使えるような理解を提供する。

概要順

名付け

関数スコープ

関数とは手続きをまとめる仕組み

関数といえば、数学でお馴染みだ。数学の関数は、入力と出力の対応関係だ。しかし、プログラミングの関数は数学の関数とは別モノだ。でも似たところもある。

プログラムとは、データを変換する過程と捉えることができる。この捉え方を「手続き型プログラミング」と呼ぶ。

データの変換って数学の関数と似ている。両者とも、入力から何かしらを出力するからだ。

データ変換の処理を「手続き」と呼ぶことにする。プログラムとは、手続きの系列として表現できる。

プログラミングの関数(以降、単に関数と書く)とは、複数の手続きを1つの手続きにまとめる仕組みだ。関数には色んな別名がある。サブルーチン、プロシージャ、メソッド、などだ。

別々の手続きを1つに統合するためには、以下が必要となる。

  • 複数の手続きを詰め込められる入れ物
  • その入れ物を指示するための名前
  • 入れ物内への入力機能(引数)
  • 入れ物外への出力機能(「戻り値」または「返り値」)

関数の例

    複数の手続きを入れ物に詰め込んでみる。

    手続き1
    手続き2
    手続き3
    
    ↓
    
    function 関数名(){
     手続き1
     手続き2
     手続き3
    }

    関数化(関数としてまとめる)によって、これらの手続きは1行だけ書けば実行できるようになった。

    関数名()
    関数名()
    関数名()

    元は9行必要な処理が3行で済むようになった。

    しかし、単に括弧内に手続きを移動させただけにしか見えない。これの何が嬉しいのだろうか。それを掘り下げるために、処理を具体的にしてみる。

    以下は三角形の面積を計算する関数だ。

    function calc_area_triangle(){
     x1, x2, x3 = 3つの座標を取得
     line1 = x1とx2による線分
      line2 = x1とx3による線分
      len1  = line1の長さ
      len2  = line2の長さ
      theta = line1とline2が成す角
      面積  = len1 * len2 * sin(theta)
    }

    プログラミング言語に標準装備されている関数を「組み込み関数」という。

    プログラミングと数学における関数の違い

    • 入力がない場合もあり得る
    • 同じ入力でも、プログラムの状態によっては異なる出力になり得る

    例えば、関数を\(f(x) = x^2\)とおく。入力を\(x=2\)とすると、\(f(2) =4\)が出力される。

    関数化により、以下のようなメリットがもたらされる。

    1. 重複の排除
    2. 処理の意味の明確化
    3. コメントが不要になる
    4. フィルタとしての可読性

    以下でmain文という言葉を用いるが、クライアント(関数を呼び出す側のプログラム)と呼び変えても差し支えない。

    1.重複の排除

    プログラムを書いていくと、同じような処理が繰り返し登場する場合があることに気づく。そこを関数化して複数回呼び出すと便利だ。私がプログラミング初心者の頃に気づいた関数のメリットだ。文法として関数を知っていても、自分で手を動かしてメリットを実感しない限り、それを使いこなすことは難しい。

    関数化による重複の排除は他にも素晴らしい副産物をもたらす。関数化で重複がなくなったことにより、処理の変更があった場合に一か所を変更するだけで済むようになる。プログラミング初心者はコピペで同じような処理を実現しがちだ。そうすると、コピペした数だけ変更を行わなければならないし、変更漏れが起きたりして苦労する。

    2.処理の意味の明確化

    関数名により処理の意図を表現することでコードの見通しが良くなる。

    関数を使わない場合、「ここで足し算して~」、「xをyに代入して」など低レベルな処理をベタ書きして1つのmain文を作成することになる。低レベルとは機械語に近いという意味で用いられる。低レベルな処理を順に読んでいっても、その意図・目的を理解するのは困難だ。

    低レベルな一連の処理の例を以下に述べる。

    1. 右足で地面を後ろ方向に蹴る
    2. 左足を前に進める
    3. 右腕を前に振る
    4. 左腕を後ろに振る

    この一連の処理は「歩く」という処理だ。この処理を「歩く」という関数でまとめると、1つ1つの低レベルな処理の意味がよく理解できる。歩くという文脈が与えられるからだ。

    main文からは「歩く」関数を呼び出すようにすれば、スッキリしたmain文になるし、「歩く」関数の詳細を知りたければ、目的が明確な低レベルな処理を追えば良い。

    関数は低レベルな処理を隠ぺいして、プログラミングの真の目的に集中させてくれる。

    3.コメントが不要になる[1]

    2.で述べた低レベルな処理をmain文にベタ書きする場合、処理内容の意図をコメントで書くと思う。しかし、コメントはコードの変更とともに修正しなければならず、やっかいである。

    そこで、関数名に意図を語らせることで、コメントは不要になるし、2重の修正も不要になる。

    4.フィルタとしての可視性[2]

    関数は引数としてデータを入力すると、そのデータを変換して返す。引数と返り値を見れば、データが変換される流れがmain文から見える。関数の詳細を知らなくても、データの変換の様子が分かるため、処理が理解しやすい。

    プログラミング言語(e.g. fortran, VBA)によっては、処理をまとめる機能としてサブルーチンと呼ばれるものがある。返り値の有無が関数とサブルーチンの違いだ。私はサブルーチンよりも関数を使うべきだと思う。

    サブルーチンは引数の情報を使って、グローバル変数を更新する。サブルーチンがグローバル変数を使用しているか確認するには、サブルーチン内部を見る必要がある。main文に書かれたサブルーチンの羅列を見ても、どのタイミングでグローバル変数が更新されたかは分からない。しかし、サブルーチンの変わりに関数を用いれば、グローバル変数の変換処理の流れを理解できる。

    質点の運動についての例(疑似言語)を以下に述べる。

    sub main()
        global x = 0
        global y = 100
        global u = 10
        global v = 0
        global g = 9.8 # 重力加速度
        global e = 0.8 # 反発係数
        global t = 0
        global dt = 1
        global MAX_TIME = 10000
    
        while t <= MAX_TIME
            sub foward(x, y, u, v, g, dt) # 時間が進み、質点が移動する
            sub collision_ground(y, e)    # 地面と衝突したら、速さvを反転する
            sub collision_wall(x, e)      # 壁と衝突したら、速さuを反転する
            t += dt

    コード1.サブルーチンによる実装

    sub main()
        # 変数定義 省略
    
        while t <= MAX_TIME
            x, y, u, v = foward(x, y, u, v, g, dt) # 時間が進み、質点が移動する
            v          = collision_ground(y, e)    # 地面と衝突したら、速さvを反転する
            u          = collision_wall(x, e)      # 壁と衝突したら、速さuを反転する
            t += dt
    

    コード2.関数による実装

    コード2の場合、どの関数がどのグローバル変数を更新しているのかはmain文を見るだけで理解できる。

    参考

    1. リーダブルコード [Dustin Boswell, Trevor Foucher]
    2. UNIXという考え方 [Mike Gancarz]

    コメント

    タイトルとURLをコピーしました