LoginSignup
38
22

More than 1 year has passed since last update.

概要

プログラミングの良い概念を理解するとプログラマーレベルが上がると考えています。
良い概念は特定のプログラミング言語に縛られないので、様々なプログラミング言語で役に立ちます。
前回は良いコードは良い名前から生まれるを紹介しました。
今回紹介する良い概念は「関数は変数である」です。

本記事のタイトルを見て「ちょっと何言ってるかわからない」と感じた方はぜひ最後まで読んでほしいです。
関数の見え方、使い方が劇的に変わります。

第一級関数

あまり聞き慣れない用語ですが、第一級関数は関数がその他の変数と同様に扱われることを表します。
JavaScriptで関数を宣言するサンプルコードを見てみましょう。

関数宣言(functionオブジェクト)

足し算をする関数(add)を宣言してみましょう。

function_add.js
function add(a, b) {
    return a + b;
}

add(100, 23); // 123

よく見かける関数宣言です。特に問題ないですね。

関数式(関数を変数に代入)

次に関数式で記述してみましょう。

const_add.js
const add = function(a, b) {
    return a + b;
}; // セミコロンを忘れずに付ける

add(100, 23); // 123

ここでは関数を変数に代入できる(エラーにならない)ということが理解できればOKです。
「これができると何が嬉しいの?」はこの後の高階関数で説明します。

高階関数

こちらもあまり聞き慣れない用語ですが、高階関数には以下の特徴(機能)があります。
・関数を引数として扱う
・関数の戻り値に関数を返す

関数を引数として扱う

今回一番伝えたい内容が「関数を引数として扱う」です。
関数も変数と同様に引数として他の関数へ渡すことができるということを知っているとプログラミングの幅が広がります。
サンプルコードを見てみましょう。

try_catch_finally.js
const init = function() {
    // TODO 初期処理を記述する
};

const calc = function() {
    // TODO 計算処理を記述する
};

try {
    preProcessing();
    init();
} catch (error) {
    // エラーが発生した場合、プロジェクト共通のログ出力処理を実行する
    logging(error);
} finally {
    // 事後処理を記述する
    postProcessing();
}

try {
    preProcessing();
    calc();
} catch (error) {
    // エラーが発生した場合、プロジェクト共通のログ出力処理を実行する
    logging(error);
} finally {
    // 事後処理を記述する
    postProcessing();
}

try { ... } catch { ... } finallyはプログラミングをしているとよく記述しますが、何度も記述していると少し煩わしさ感じることもあります。
上記サンプルコードではtry { ... } catch { ... } finallyの中でinit()とcalc()が違うだけです。
関数を引数として扱う関数を定義してtry { ... } catch { ... } finallyを共通化してみましょう。

do_processing.js
const init = function() {
    // TODO 初期処理を記述する
};

const calc = function() {
    // TODO 計算処理を記述する
};

const doProcessing = function(processing) {
    try {
        // 事前処理を実行する
        preProcessing();
        // 引数で受け取った関数を実行する
        processing();
    } catch (error) {
        // エラーが発生した場合、プロジェクト共通のログ出力処理を実行する
        logging(error);
    } finally {
        // 事後処理を記述する
        postProcessing();
    }
};

doProcessing(init); // 引数としてinitを渡す
doProcessing(calc); // 引数としてcalcを渡す

try { ... } catch { ... } finallyの部分が共通化(コードで1箇所しかない)できました。
以前の記事で紹介したOAOO原則に従い、重複したコードがなくなりました。
私は初めて上記のようなコードを見たときに「関数にこんな使い方あるのか!」と感動しました。

関数の戻り値に関数を返す

戻り値に変数を返すように戻り値に関数を返すことができます。
足し算をする関数(add)を修正したサンプルコードを見てみましょう。

add.js
const add = function(a) {
    return function(b) {
        return a + b;
    };
};

add(200)(67); // 267

const add100 = add(100);

add100(23); // 123
add100(45); // 145

上記のようなコードをカリー化と言います。
カリー化についての詳細は割愛しますが、関数型プログラミング言語ではよく見かけます。

まとめ

今回は関数は変数であるを紹介しました。
関数はとても奥が深く色んな概念(コールバック関数、クロージャ、カリー化、部分適用、関数合成など)があります。
関数は変数であるを理解してプログラマーレベルが上がったと感じていただければ幸いです。

最後まで読んでいただきありがとうございます。

38
22
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
22