
関数の定義と引数
この節の目的は、同じ処理を何度も書かずに再利用できるようにする考え方を理解することです。Dart では、そのために 関数 を使います。関数の考え方自体はかなり古く、1956年の FORTRAN の参考マニュアルには FUNCTION と SUBROUTINE が記載されており、1960年の ALGOL 60 改訂報告では procedure declarations と formal / actual parameters が整理されています。つまり、今学ぶ関数と引数は、プログラミングのかなり長い歴史を持つ基本概念です。
Dart の現在の関数設計は、単に「処理をまとめる」だけではありません。型安全性、型推論、null safety、名前付き引数、必須 named parameters などを通して、読みやすく、間違いにくく、再利用しやすい関数を書く方向へ進化しています。Dart 公式は、関数で名前付き引数、位置引数、オプション引数、デフォルト値、required を使えることを説明しており、null safety の導入によって non-nullable 型の扱いもより明確になっています。
1. 関数とは何か
関数とは、ひとまとまりの処理に名前をつけたものです。たとえば「朝のあいさつを表示する」「税込み価格を計算する」「点数から評価を返す」のように、何度も使いたい処理を一か所へまとめられます。これにより、同じコードを何回も書かずに済み、あとで修正もしやすくなります。Dart 公式でも、関数は top-level、class の中、または他の関数の中に定義できる基本要素として扱われています。
たとえば、毎回あいさつ文を直接書く代わりに、関数へまとめるとこうなります。
void greet() {
print('おはようございます');
}
void main() {
greet();
greet();
}
このコードでは、greet() という名前の関数を2回呼んでいます。
出力は同じでも、処理をまとめたことで読みやすさが上がっています。
2. 関数の歴史
プログラミング史の初期に、関数やサブルーチンの考え方を実用化した代表例が FORTRAN です。1956年10月15日付の FORTRAN Programmer’s Reference Manual には、FUNCTION と SUBROUTINE が載っており、関数が値を返す手続き、サブルーチンが手続きのまとまりとして使われていたことが分かります。この FORTRAN は John Backus のチームが IBM で開発しました。
さらに 1960年の ALGOL 60 改訂報告では、手続きを表す procedure declarations、実引数と仮引数にあたる actual parameters と formal parameters が整理されました。つまり、「処理へ名前をつける」だけでなく、「外から値を渡して使う」という、今の関数の骨格がこの時代にかなり明確になりました。ALGOL 60 の報告書は Peter Naur が編集責任者の一人としてまとめています。
初心者向けに言い換えると、関数は昔から「処理を部品化するための仕組み」として育ってきた、ということです。今の Dart の関数も、その流れの上にあります。
3. Dart の関数の基本形
Dart の関数は、基本的に次の形で書きます。
戻り値の型 関数名(引数) {
処理
}
たとえば、名前を受け取ってあいさつする関数はこう書けます。
void greet(String name) {
print('こんにちは、$name さん');
}
void main() {
greet('Aoi');
}
ここでのポイントは三つです。
void戻り値がないことを表します。つまり、この関数は値を返さず、表示だけします。greet関数名です。何をする関数か分かる名前にします。String name引数です。外から受け取る値で、ここでは文字列の名前を受け取ります。 Dart 公式でも、関数は引数リストを持ち、必要に応じて値を返すものとして説明されています。
4. 引数とは何か
引数とは、関数へ外から渡す値です。
料理でいえば材料、ゲームでいえば設定値、学校のプリント作成でいえばタイトルや日付のようなものです。
たとえば、同じ「点数表示」の関数でも、毎回違う点数を使いたいときは引数が必要です。
void showScore(int score) {
print('点数は $score 点です');
}
void main() {
showScore(80);
showScore(95);
}
このコードでは、80 や 95 が実際に渡す値です。
関数定義側の int score は、受け取る側の名前です。
ここで初心者が混乱しやすい言葉を整理します。
- 仮引数
関数定義に書く受け取り用の名前
例:
int score - 実引数
実際に関数へ渡す値
例:
80この考え方は、ALGOL 60 で formal / actual parameters として整理された流れに続いています。
5. 引数が複数ある場合
引数は一つとは限りません。
たとえば、二つの数を受け取って表示したいときは、カンマで並べます。
void showRectangle(int width, int height) {
print('横は $width、縦は $height です');
}
void main() {
showRectangle(10, 5);
}
このとき、順番が大事です。
showRectangle(10, 5) は、width = 10、height = 5 です。showRectangle(5, 10) と書くと意味が変わります。
つまり、通常の引数は 位置で意味が決まる のです。
これを 位置引数 positional parameters といいます。Dart 公式も、通常のパラメータを positional parameters として扱っています。
6. 戻り値とは何か
関数は、表示だけでなく、計算結果を返す こともできます。 返す値を 戻り値 return value といいます。
int add(int a, int b) {
return a + b;
}
void main() {
int result = add(3, 4);
print(result);
}
このコードでは、add 関数が 3 + 4 の結果を返しています。return は「この値を返します」という意味です。
ここで大切なのは、戻り値の型を関数名の前へ書くことです。
int add(...) なので、「この関数は最終的に整数を返す」と分かります。
もし文字列を返すなら String、真偽値なら bool になります。Dart 公式も、関数の戻り値型を明示するのが基本であると説明しています。
7. voidと値を返す関数の違い
ここは初心者がかなり混乱しやすい点です。
voidの関数
値を返さない 例: 表示する、送信する、記録する
intやStringの関数
値を返す 例: 合計する、名前を作る、判定結果を返す 比べてみます。
void sayHello() {
print('Hello');
}
String makeGreeting(String name) {
return 'Hello, $name';
}
void main() {
sayHello();
String text = makeGreeting('Rin');
print(text);
}
sayHello() は表示するだけです。makeGreeting() は文字列を返し、その返り値を変数 text へ入れています。
8. Dart の名前付き引数
Dart の関数でとても便利なのが 名前付き引数 named parameters です。
これは、引数を順番ではなく名前で渡せる仕組みです。Dart 公式では、名前付き引数は {} を使って定義し、デフォルトでは optional だが required を付けると必須にできると説明しています。
void introduce({required String name, int age = 16}) {
print('$name さんは $age 歳です');
}
void main() {
introduce(name: 'Aoi');
introduce(name: 'Rin', age: 17);
}
ここでは、
nameはrequiredなので必須ageは省略可能で、省略時は16となります。
名前付き引数の良いところは、読みやすいことです。
introduce(name: 'Aoi', age: 17) と書けば、どの値が何を意味するかすぐ分かります。
9. オプション引数とデフォルト値
Dart では、すべての引数を毎回必ず書かなくてもよい場合があります。
たとえば、設定値や補助的な情報は省略可能にしたいことがあります。
名前付き引数での例です。
void showMessage(String message, {bool isImportant = false}) {
if (isImportant) {
print('【重要】$message');
} else {
print(message);
}
}
void main() {
showMessage('明日はテストです');
showMessage('提出期限は今日です', isImportant: true);
}
Dart 公式によると、名前付き引数は required を付けない限り optional です。そして default value を設定できます。default value や required がない named parameter は default が null になるため、型は nullable である必要があります。
10. null safety と引数
Dart は sound null safety を持つので、引数でも null の扱いが明確です。
non-nullable 型の引数は、原則として null を受け取れません。
nullable にしたいなら ? を付けます。Dart 公式は、non-nullable types must be initialized and can only be assigned non-null values と説明しています。
void printNickname(String? nickname) {
if (nickname != null) {
print('ニックネームは $nickname です');
} else {
print('ニックネームは未設定です');
}
}
void main() {
printNickname('Sora');
printNickname(null);
}
このコードでは、String? と書いているので null を渡せます。
その代わり、使う前に null かどうかを確認しています。
11. 関数を使うと何がうれしいのか
関数のうれしさは、大きく三つあります。
11-1. 同じ処理を何度も書かなくてよい
String makeTitle(String subject) {
return '【$subject のお知らせ】';
}
void main() {
print(makeTitle('数学'));
print(makeTitle('英語'));
}
11-2. 直す場所が一か所で済む
タイトルの形式を変えたくなったら、関数の中だけ直せばよいです。
11-3. 処理に意味を持たせられる
makeTitle() という名前があるだけで、「これはタイトルを作る処理だ」と分かります。
つまり関数は、コードに説明を埋め込む仕組み でもあります。
12. 実践
実践1: 二人の名前をあいさつする
void greet(String name) {
print('こんにちは、$name さん');
}
void main() {
greet('Aoi');
greet('Rin');
}
実践2: 合計を返す
int add(int a, int b) {
return a + b;
}
void main() {
print(add(5, 7));
}
実践3: 名前付き引数を使う
void createProfile({required String name, String club = '未所属'}) {
print('$name さん / 部活: $club');
}
void main() {
createProfile(name: 'Mio');
createProfile(name: 'Kenta', club: 'Soccer');
}
実践4: nullable な引数を扱う
void showComment(String? comment) {
if (comment != null) {
print('コメント: $comment');
} else {
print('コメントなし');
}
}
void main() {
showComment('がんばりました');
showComment(null);
}
13. 練習問題
問題1
関数とは何ですか。
初心者向けに一文で説明してください。
問題2
次のコードは何を表示しますか。
void greet(String name) {
print('Hello, $name');
}
void main() {
greet('Aoi');
}
問題3
次のコードの戻り値は何ですか。
int multiply(int a, int b) {
return a * b;
}
void main() {
print(multiply(3, 4));
}
問題4
次のうち、仮引数 はどれですか。
void showScore(int score) {
print(score);
}
void main() {
showScore(90);
}
90scoreshowScore
問題5
次のコードは何を表示しますか。
void introduce({required String name, int age = 16}) {
print('$name は $age 歳です');
}
void main() {
introduce(name: 'Rin');
}
問題6
String? nickname の ? は何を表しますか。
問題7
次の関数を読んで、何をしている関数か説明してください。
String makeBadge(String name) {
return '名札: $name';
}
14. 練習問題の答え
答え1
例です。
「関数は、ひとまとまりの処理に名前をつけて、何度も使えるようにしたものです。」
答え2
Hello, Aoi を表示します。
答え3
12 です。3 * 4 の結果を return しているからです。
答え4
正解は **2. **score です。
score は関数定義側で受け取る名前なので仮引数です。
90 は実引数です。formal / actual parameters の区別は ALGOL 60 の報告にも見られます。
答え5
Rin は 16 歳です を表示します。
age を省略したので default value の 16 が使われます。Dart の named parameters は optional にでき、default values を設定できます。
答え6
nickname が String かもしれないし null かもしれない ことを表します。
これは nullable type です。Dart の null safety では non-nullable と nullable を区別します。
答え7
name を受け取り、名札: 名前 という文字列を作って返す関数です。
15. まとめ
この節では、関数の定義と引数を学びました。関数は、処理へ名前をつけて再利用しやすくする仕組みです。引数は関数へ外から渡す値で、仮引数と実引数の区別があります。戻り値を使えば、計算結果や作った文字列を呼び出し元へ返せます。Dart では位置引数だけでなく、名前付き引数、required、default values、nullable / non-nullable な型を使って、読みやすく安全な関数を書けます。
歴史的には、FORTRAN の FUNCTION / SUBROUTINE、ALGOL 60 の procedure declarations と formal / actual parameters が、現在の関数設計の基盤になっています。今みなさんが Dart で書く関数は、そうした長い流れのうえにある、現代的で型安全な関数です。次節では、今回少し触れた 戻り値と型設計 をもう少し丁寧に見ていきます。
参考文献
- https://dart.dev/language/functions
- https://dart.dev/overview
- https://dart.dev/null-safety
- https://scc.ustc.edu.cn/zlsc/cxyy/200910/W020100308600976088848.pdf
- https://archive.computerhistory.org/resources/text/Fortran/102649787.05.01.acc.pdf
- https://www.algol60.org/reports/algol60_rr.pdf
- https://softwarepreservation.computerhistory.org/ALGOL/report/Algol60_revised_report_CACM.pdf
- https://www.cs.toronto.edu/~bor/199y08/backus-fortran-copy.pdf