ローカル関数について

初めまして。
PHR事業開発部の村越です。NOBORIではPHRアプリの開発に携わっています。

今回は、ローカル関数について書いていこうと思います。

■ローカル関数とは

ローカル関数とは、関数の中に宣言された関数のことで、以下のような書き方になります。
サンプルはDartになりますが、Javaなど他の言語でも同様に使用することが出来ます。

func() {
  _localFunc() {}

  _localFunc();
}

■ローカル関数のメリット

ローカル関数の特徴は、以下の2つがあります。

・定義した関数の内部でしか呼ぶことが出来ない
・宣言した関数内の変数を参照できる

それぞれをサンプルコードを交えながら見ていきましょう。

ローカル関数は、関数の内部で宣言しているので、当然ですが宣言した関数内でしか呼び出すことが出来ません。

func() {
  _localFunc() {}

  _localFunc(); //これは呼び出せる
}
 
func2() {
  _localFunc(); //これは呼び出せない
}

関数内のロジックを切り出す際に、private関数だと同じクラス内で呼び出される可能性があり、処理を変更する際などに影響範囲が大きくなる可能性がありますが、ローカル関数ならば宣言した関数内でしか呼び出せないため、局所的な処理を切り出す場合はこちらの方が安全かと思われます。

func() {
  int _localFunc(int x, int y) {
    // func関数内からしか呼び出されないため
    // 影響範囲の確認がほぼ不要
    return x + y;
  }

  var result1 = _func2(1, 2);
  var result2 = _localFunc(1, 2);
}

int _func2(int x, int y) {
  // こちらは同クラス内から呼び出される可能性があるため
  // 処理を変更する際に影響範囲の確認が必要
  return x + y; 
}

上記の例のような、処理が単純で共通で使用しそうなメソッドであればprivateで定義でいいと思いますが、
他で使うことがなさそう、または使用箇所のロジックに依存する処理を切り出す場合はローカル関数にするのを考慮してもいいかと思います。

次に、ローカル関数では宣言した関数内の変数を参照できます。

func(int param) {
  var _localParam = 3;
   
  int _localFunc(int x, int y) {
    // func関数の引数である param
    // func関数内で定義された _localParam を参照できる
    return x + y + _localParam + param;
  }

  var result = _localFunc(1, 2);
}

例えば、以下のようなコードがあります。

func(String job) {
  var isDelete = getIsDelete();

  func2("田中", 18, job, isDelete);
  func2("鈴木", 20, job, isDelete);
  func2("佐藤", 31, job, isDelete);
}

func2(String name, int age, String job, bool isDelete) {
  Test test = Test(name, age, job, isDelete);
}

上記の例は、jobは引数の値で固定、isDeleteは処理内で取得した値で固定し、name、ageが異なるデータを作成する処理を別関数に切り出しています。

jobの値はfunc関数の引数の値で固定されていますが、func2関数の引数で指定できるため、コードを読む際に、引数の値以外が入る可能性があるのではないかという考えを持たせてしまい、可読性が下がっています。
また、この程度の数であればまだわかりますが、例えばTestに入れる固定のパラメータが追加された場合、func2関数の引数の数が増えていき、可読性が下がる恐れがあります。

これをローカル関数を用いると、このように記載が出来ます。

func(String job) {
  var isDelete = getIsDelete();

  _localFunc(String name, int age) {
    // job、isDeleteを参照できるので、引数で渡す必要がない
    Test test = Test(name, age, job, isDelete);
  }

  _localFunc("田中", 18);
  _localFunc("鈴木", 20);
  _localFunc("佐藤", 31);
}

先程より、jobの値が引数の値をそのまま参照しているので、別の値が入ることがないことがわかり、可読性が上がっていると思います。
また、このように記載しておけば、固定の値が増えたとしても、_localFunc関数の引数は増えないため、可読性が下がる恐れも低くなります。

もし、先程の例で、func2が別の箇所から使用している場合は、以下のように記載すると良いと思います。

func(String job) {
  var isDelete = getIsDelete();

  _localFunc(String name, int age) {
    // job、isDeleteを参照できるので、引数で渡す必要がない
    func2(name, age, job, isDelete);
  }

  _localFunc("田中", 18);
  _localFunc("鈴木", 20);
  _localFunc("佐藤", 31);
}

func2(String name, int age, String job, bool isDelete) {
  Test test = Test(name, age, job, isDelete);
}

■最後に

ローカル関数について記載してみましたが、いかがだったでしょうか?

あまり見ない書き方かなと思ったので、こんな書き方もあるという周知も兼ねてこの記事を書かせて頂きました。
使う機会多くはないかもしれませんが、覚えておくとコードを簡略化出来たり、便利な場合もあるかと思います。

使う際には通常の関数と区別するために__を付けるなどの規約を設けてもいいかと思います。

今回は以上となります。
今後とも「NOBORI」をよろしくお願い致します。

村越