関数
Dart は真のオブジェクト指向言語であるため、関数でさえオブジェクトであり、Function という型を持ちます。これは、関数を変数に代入したり、他の関数の引数として渡したりできることを意味します。また、Dart クラスのインスタンスを関数であるかのように呼び出すこともできます。詳細については、呼び出し可能オブジェクトを参照してください。
以下は関数を実装する例です
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
Effective Dart ではパブリック API の型アノテーションが推奨されていますが、型を省略しても関数は機能します
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
1 つの式のみを含む関数には、短縮構文を使用できます
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> expr
構文は、{ return expr; }
の短縮形です。=>
表記は、アロー構文と呼ばれることがあります。
パラメーター
#関数は、任意の数の必須の位置パラメーターを持つことができます。これらに続けて、名前付きパラメーターまたはオプションの位置パラメーター (両方は不可) を指定できます。
関数に引数を渡すとき、または関数パラメーターを定義するときに、末尾のコンマを使用できます。
名前付きパラメーター
#名前付きパラメーターは、required
として明示的にマークされない限り、オプションです。
関数を定義するときは、{param1, param2, …}
を使用して名前付きパラメーターを指定します。デフォルト値を指定しない場合、または名前付きパラメーターを required
としてマークしない場合、それらのデフォルト値は null
になるため、それらの型は null 許容である必要があります。
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool? bold, bool? hidden}) {...}
関数を呼び出すときは、paramName: value
を使用して名前付き引数を指定できます。例:
enableFlags(bold: true, hidden: false);
null
以外の名前付きパラメーターのデフォルト値を定義するには、=
を使用してデフォルト値を指定します。指定された値は、コンパイル時定数である必要があります。例:
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}
// bold will be true; hidden will be false.
enableFlags(bold: true);
代わりに、名前付きパラメーターを必須にして、呼び出し元がパラメーターの値を指定するように強制したい場合は、required
で注釈を付けます
const Scrollbar({super.key, required Widget child});
child
引数を指定せずに Scrollbar
を作成しようとすると、アナライザーが問題を報告します。
位置引数を最初に配置したい場合もありますが、Dart では必須ではありません。Dart では、API に合わせて、引数リストの任意の場所に名前付き引数を配置できます。
repeat(times: 2, () {
...
});
オプションの位置パラメーター
#関数パラメーターのセットを []
で囲むと、それらはオプションの位置パラメーターとしてマークされます。デフォルト値を指定しない場合、それらのデフォルト値は null
になるため、それらの型は null 許容である必要があります
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
以下は、オプションのパラメーターなしでこの関数を呼び出す例です
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
以下は、3 番目のパラメーターを指定してこの関数を呼び出す例です
assert(say('Bob', 'Howdy', 'smoke signal') ==
'Bob says Howdy with a smoke signal');
null
以外のオプションの位置パラメーターのデフォルト値を定義するには、=
を使用してデフォルト値を指定します。指定された値は、コンパイル時定数である必要があります。例:
String say(String from, String msg, [String device = 'carrier pigeon']) {
var result = '$from says $msg with a $device';
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
main() 関数
#すべてのアプリには、アプリへのエントリポイントとして機能するトップレベルの main()
関数が必要です。main()
関数は void
を返し、引数のためのオプションの List<String>
パラメーターを持ちます。
以下は単純な main()
関数です
void main() {
print('Hello, World!');
}
以下は、引数を取るコマンドラインアプリの main()
関数の例です
// Run the app like this: dart run args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
args ライブラリを使用して、コマンドライン引数を定義および解析できます。
第一級オブジェクトとしての関数
#関数を別の関数のパラメーターとして渡すことができます。例:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
次のように、関数を変数に代入することもできます
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
この例では、匿名関数を使用しています。詳細については、次のセクションで説明します。
関数型
#関数型として知られる関数の型を指定できます。関数型は、関数宣言ヘッダーで関数名をキーワード Function
に置き換えることによって取得されます。さらに、位置パラメーターの名前を省略することは許可されていますが、名前付きパラメーターの名前を省略することはできません。例:
void greet(String name, {String greeting = 'Hello'}) =>
print('$greeting $name!');
// Store `greet` in a variable and call it.
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
匿名関数
#main()
や printElement()
のように、ほとんどの関数には名前を付けますが、名前のない関数を作成することもできます。これらの関数は、匿名関数、ラムダ、またはクロージャと呼ばれます。
匿名関数は、名前付き関数に似ています。その理由は、以下のものがあるからです
- 0 個以上のパラメーター (コンマ区切り)
- かっこで囲まれたオプションの型アノテーション
次のコードブロックには、関数の本体が含まれています
([[Type] param1[, ...]]) {
codeBlock;
}
次の例では、型指定されていないパラメーター item
を持つ匿名関数を定義します。匿名関数は、それを map
関数に渡します。リスト内の各項目に対して呼び出される map
関数は、各文字列を大文字に変換します。次に、forEach
に渡される匿名関数は、変換された各文字列とその長さを出力します。
const list = ['apples', 'bananas', 'oranges'];
var uppercaseList = list.map((item) {
return item.toUpperCase();
}).toList();
// Convert to list after mapping
for (var item in uppercaseList) {
print('$item: ${item.length}');
}
コードを実行するには、[実行] をクリックします。
void main() {
const list = ['apples', 'bananas', 'oranges'];
var uppercaseList = list.map((item) {
return item.toUpperCase();
}).toList();
// Convert to list after mapping
for (var item in uppercaseList) {
print('$item: ${item.length}');
}
}
関数に 1 つの式または return ステートメントのみが含まれている場合は、アロー表記を使用して短縮できます。次の行を DartPad に貼り付けて、[実行] をクリックして、機能的に同等であることを確認します。
var uppercaseList = list.map((item) => item.toUpperCase()).toList();
uppercaseList.forEach((item) => print('$item: ${item.length}'));
レキシカルスコープ
#Dart は、コードのレイアウトに基づいて変数のスコープを決定します。この機能を持つプログラミング言語は、レキシカルスコープ言語と呼ばれます。「外側の中括弧をたどる」ことで、変数がスコープ内にあるかどうかを確認できます。
例: 各スコープレベルに変数がある一連のネストされた関数
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
nestedFunction()
メソッドは、トップレベルまでのすべてのレベルの変数を使用できます。
レキシカルクロージャ
#関数がスコープ外にあるときに、レキシカルスコープ内の変数にアクセスできる関数オブジェクトは、クロージャと呼ばれます。
関数は、外側のスコープで定義された変数をクロージャとして取り込むことができます。次の例では、makeAdder()
は変数addBy
をキャプチャします。返された関数がどこへ行こうとも、addBy
を覚えています。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
return (int i) => addBy + i;
}
void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
ティアオフ
#関数、メソッド、または名前付きコンストラクターを括弧なしで参照すると、Dartはティアオフを作成します。これは、関数と同じパラメーターを受け取り、呼び出されると基になる関数を呼び出すクロージャです。コードが、クロージャが受け入れるのと同じパラメーターで名前付き関数を呼び出すクロージャを必要とする場合、ラムダで呼び出しをラップしないでください。ティアオフを使用してください。
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
// Function tear-off
charCodes.forEach(print);
// Method tear-off
charCodes.forEach(buffer.write);
// Function lambda
charCodes.forEach((code) {
print(code);
});
// Method lambda
charCodes.forEach((code) {
buffer.write(code);
});
関数の等価性をテストする
#以下は、トップレベル関数、静的メソッド、およびインスタンスメソッドの等価性をテストする例です。
void foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
void main() {
Function x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = A(); // Instance #1 of A
var w = A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
戻り値
#すべての関数は値を返します。戻り値が指定されていない場合、ステートメントreturn null;
が関数本体に暗黙的に追加されます。
foo() {}
assert(foo() == null);
関数で複数の値を返すには、レコードに値を集約します。
(String, int) foo() {
return ('something', 42);
}
ジェネレーター
#値のシーケンスを遅延生成する必要がある場合は、ジェネレーター関数の使用を検討してください。Dartには、2種類のジェネレーター関数の組み込みサポートがあります。
同期ジェネレーター関数を実装するには、関数本体をsync*
としてマークし、値を配信するためにyield
ステートメントを使用します。
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
非同期ジェネレーター関数を実装するには、関数本体をasync*
としてマークし、値を配信するためにyield
ステートメントを使用します。
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
ジェネレーターが再帰的な場合は、yield*
を使用することでパフォーマンスを向上させることができます。
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
外部関数
#外部関数は、その本体が宣言とは別に実装されている関数です。次のように、関数宣言の前にexternal
キーワードを含めます。
external void someFunc(int i);
外部関数の実装は、別のDartライブラリから、またはより一般的には、別の言語から取得できます。相互運用コンテキストでは、external
は外部関数または値の型情報を導入し、Dartで使用できるようにします。実装と使用法はプラットフォームに大きく依存するため、たとえば、CまたはJavaScriptの相互運用ドキュメントをチェックして、詳細を確認してください。
外部関数は、トップレベル関数、インスタンスメソッド、ゲッターまたはセッター、またはリダイレクトしないコンストラクターにすることができます。インスタンス変数もexternal
にすることができ、これは外部ゲッター(および変数がfinal
でない場合は)外部セッターと同等です。
特に明記されていない限り、このサイトのドキュメントはDart 3.5.3を反映しています。ページ最終更新日:2024年10月16日。 ソースを表示 または 問題を報告する。