言語編 |
関数は実行可能なアクションの抽象化である。Dartでは関数もオブジェクトでその型はFunctionである。即ちDartの関数は第1級関数(first-class function)である。従って後述のように、ある関数を別の関数にパラメタとして渡すことも可能である。またあるDartのクラスのインスタンスをあたかもそれが関数であったかのように呼び出すことも可能である。その詳細は「関数のエミュレーション (Emulating functions)」の章を参照されたい。
関数には関数宣言(function declarations)、メソッド(methods)、ゲッタ(getters)、セッタ(setters)、コンストラクタ(constructors)、及び関数リテラル(function literals)がある。
関数宣言にはライブラリのトップ・レベル(即ちクラスのメンバでない)にある関数(例えばmain()でトップ・レベル関数という)、あるクラスに結び付けられた関数(static関数とかクラス関数という)、あるオブジェクトに結び付けられた関数(インスタンス・メソッドという)、および関数の内部に宣言されたローカル関数がある。関数の中にネストした関数を持つことも可能である。
JavaScriptと違って定義の為のfunctionといったキーワードは存在しない。
総ての関数はひとつのシグネチュアとひとつのボディを持つ。シグネチュアはその関数の仮パラメタたち(formal parameters)、及びその名前と戻りの型を記述する。ボディはその関数によって実行される文たち(statements)が入っているひとつのブロック文(block statement)である。形式 => e の形式の関数ボディは{return e;}の形式のボディと等価である。
ある関数の最後の文がreturn文でないときは、その関数ボディに対してはreturn null;という文が暗示的に付加される。
また関数宣言が戻り値の型を明示的に指定していないときは、その型はdynamic型として扱われる。ここにdynamicは未知(unknown)という意味の型である。
以下はある関数の実装例である:
|
関数宣言に型アノテーションを付すことは推奨されている(Effective Dart参照)ことだが、この関数は型を省略しても動作する:
|
単に一つだけの式からなる関数にはより簡素化した構文が使える。これは一行関数(one-line function)と呼ばれる最も簡単な関数宣言で:
|
=> exprという構文は{ return expr; }の短縮形である。=>表記はしばしば矢印構文(arrow syntax)と呼ばれる。
注意:式(文ではなくて)のみが矢印(=>)とセミコロン(;)の間に置かれる。例えば、if文はそこにおけないが、条件式は使える。
関数はふたつのタイプのパラメタたち:即ち必須(required)とオプショナル(optional)のパラメタたちを持てる。必須パラメタたちは最初にリストされ、以下に任意のオプショナルなパラメタたちが置かれる。名前付きオプショナル・パラメタたちは@requiredアノテーションでマークできる。
必須パラメタとオプショナルなパラメタは以下のように整理される:
パラメタの形式 |
必須パラメタ |
オプショナルな位置的パラメタ |
オプショナルな名前付きパラメタ |
宣言時のパラメタ・リストの形式 |
パラメタ間をカンマで区切る |
[]で囲ったカンマで区切られたパラメタたち |
{}で囲ったカンマで区切られた名前付きのパラメタたち。名前付きのパラメタは名前:変数)の形式となる |
宣言時のデフォルト値指定 |
不可 |
仮パラメタ=値で指定 |
名前=値で指定 |
呼び出し時の引数 |
指定された順にセット |
指定された順にセットするが終わりのパラメタから連続してデフォルト値を使うときはそれらを省略可 |
名前=値で指定し、セット順は問わない。省略可 |
詳細は次節を参照のこと。
オプショナルなパラメタは位置的(positional)なものまたは名前付き(named)のいずれかがとなるが、双方ともにはなれない。
ある関数を呼ぶ際に、paramName: valueを使って名前付きパラメタを指定できる。例えば:
|
関数を定義する際名前付きパラメタを指定するときはそれらを波括弧で囲んだ{param1, param2, …}を使う:
|
Flutterでのインスタンス生成式は複雑になり得るので、ウィジェット・コンストラクタはもっぱら名前付きパラメタを使う。そうするとインスタンス生成式が読みやすくなる。
単にFlutterだけでなく、どの環境のDartコード内でも名前付きパラメタを付すことができる。例えば:
|
Scrollbarが生成される際、このchild引数が欠けていると問題を報告する。
Requiredはmetaパッケージの中で定義されている。 package:meta/meta.dartを直接インポートするか、またはFlutterの package:flutter/material.dartのようなmetaをエクスポートする別のパッケージをインポートする。
角括弧[]内に関数パラメタたちを包むとそれはオプショナルな位置的パラメタたちであることを示す:
|
以下はこの関数をオプショナルなパラメタなしで呼んだ例である:
|
また以下はこの関数を3番目のパラメタ付きで呼んだ例である:
|
もう一つの例を示そう:
|
この場合は呼び出しにあたっては位置的パラメタには名前が付すことができず、名前付きでは指定できない。
この例では:
connectToServerという関数は3つの位置的パラメタが使われている
呼び出すときはこの3つのパラメタをその順にセットする
最初のauthKeyは必須パラメタだが、ipとportはオプショナルでデフォルト値がセットされている
ipをデフォルト値以外の値にするときは2番目のパラメタとしてセットする
portをデフォルト値以外の値にしたいときは、ipの値もセットしなければならない
名前付き及び位置的パラメタたちともに対しデフォルト値を定義するのに=が使える。デフォルト値はコンパイル時常数でなければならない。デフォルト値が与えられていない場合は、そのデフォルト値はnullである。
以下は名前付きパラメタたちにデフォルト値を設定している例である:
|
廃止対象の注意:名前付きパラメタたちにデフォルト値をセットするのに=ではなくてコロン(:)が使われている可能性がある。その理由は当初は名前付きパラメタたちには:のみがサポートされていた為である。このサポートは廃止対象になる可能性があるので、デフォルト値の位置的パラメタたちには=を使用することを進める。
以下は位置的パラメタたちにデフォルト値を設定している例である:
|
リストとマップもデフォルト値として渡すことができる。以下の例はdoStuff()という関数を定義しているが、listパラメタにはデフォルトのリストを、giftsパラメタにはデフォルトのマップを指定している:
|
各アプリケーションはそのアプリの入口の開始点となるトップ・レベルのmain()関数を持っていなければならない。このmain()関数はvoidを返し、引数としてオプショナルなList<String>パラメタを持つ。
以下はウェブ・アプリのためのmain()関数の例である:
|
注意:上のコードのなかの..構文はカスケードと呼ばれる。カスケードを使うと単一のオブジェクトのメンバたちに複数の操作を行わせることができる。
以下は引数を受け付けるコマンド行アプリのためのmain()関数の例である:
|
コマンド行の引数を定義し解析するためのargsライブラリを使うことが可能である。
ある関数を別の関数のパラメタとして渡すことができる。例えば:
|
これはlistの各要素ごとにprintElementという関数を実行させている。
ある関数を次のようにある変数に代入することもできる:
|
この例は匿名関数(anonymous function)を使っている。詳細は「匿名関数」の節で示されている。
関数内関数はローカル関数(local function)とも呼ばれ、関数内に関数を置くことは可能である。次の例ではimportantify(重要性強調)という内部関数が定義されている:
|
関数内関数はそれが呼ばれる前に定義されていなければならない。
関数を再帰的に使うことも可能である。これは階乗計算やフィボナッチ関数の計算等でよく使われる。
殆どの関数はmain()あるいはprintElement()といった名前が付されている。しかし匿名関数あるいは時にはラムダ(lambda)、クロージャ(closure)あるいは関数リテラル(function literal)とも呼ばれる名前のない関数を作ることも可能である。ある匿名関数をある変数に代入し、例えばあるコレクションからそれを追加あるいは削除できるといったことが可能である。
匿名関数は非同期のイベントやストリームの処理ではコールバック関数として頻繁に使用される。例えばFutureのthenメソッドのAPIを見るとonValueとonErrorのコールバック関数が登録される。これらはライブラリ編の「非同期プログラミング」の章を参考にされたい。
匿名関数は名前付き関数と同じに見えるーゼロまたはそれ以上のパラメタがカンマで区切られており、オプショナルな型アノテーションがあり、括弧で囲まれている。
関数のボディは波括弧で囲まれたコード・ブロックが含まれる:
|
以下の例は型付けされていないパラメタのitemを含んだ匿名関数を定義している。この関数はlistのなかの各itemごとに呼び出され、指定されたインデックスにある文字列の値をプリントする。
|
DartPadで確認されたい:
この例のようにその関数がただ一つの文だけのときは矢印構文が使える。以下の行を上のコードに追加してDartPadで同じ結果が得られることを確認されたい。
|
次の例では匿名関数を変数として扱っている:
|
関数を使っても関数リテラルを使っても呼び出しは同じで、また結果も同じである。関数リテラルは名前は不要である。その代わりFunction型のオブジェクトとしてdepositあるいはwithdrawという変数に代入されている。従って関数リテラルのオブジェクトは実行時に生成される。関数及び関数リテラルともにオブジェクトであるから別の関数の引数に使ったり、また戻しの値として使うことが可能になる。
関数式を()で括ったものは関数識別子と同じように扱える。
以下の例では2つの値の平均値を求める関数リテラルに10と8という2つの値を与えて実行し、その結果をプリントしている。つまりprintというメソッドの中の式として関数リテラルが組み入れられている:
|
printの行は単行関数式を使って次のようにも書ける:
|
関数のパラメタに関数リテラルとデフォルト値を含むことができる。次の例ではconsoleOutは結果の文字列を表示する為のメソッド(ラッパー)・オブジェクトだが、これは呼び出し側で実体化する。またtoFahrenheitはどちらに変換するかを指定するが、デフォルトでは摂氏から華氏への変換が実行される。呼び出し側でこれを変更するときはtoFahrenheit: falseという具合に名前つきで指定する:
|
関数及び関数リテラルは再帰可能であるが、処理速度は一般に遅くなる。次のコードは関数の再帰可能性を示す為に良く使われるパタンだが、最初に呼ばれたときのnという変数とその関数の中で呼ばれた次の関数オブジェクトのnとは別のものであることを使っており、ややトリッキーである。
|
|
もうひとつ良く引用されるサンプルはFibonacci数列である。これはnが増えると計算時間が増えるので、時間がかかる処理のシミュレーションによく使われる:
|
Dartは静的にスコープ付けされた言語で、このことは変数のスコープは静的に、単にそのコードのレイアウトによって決まることを意味する。もしある変数がスコープ内にあるかどうかを調べたいときは「それが定義されているその波括弧のなかか」を追って行けば良い。
以下は各スコープのレベルにある変数が記されているネストした関数の例である:
|
nestedFunction()がどのようにトップ・レベルに至る各レベル(つまりnestedFunction内→myFunction内→main内→トップ・レベル)からの変数が使えるかを確認されたい。
クロージャは、その関数がそのオリジナルのスコープの外で使われていたとしても、その静的スコープ内の変数にアクセスできる関数オブジェクトのことを言う。
関数はそれを囲んでいるスコープ内で定義された変数たちを包含できる。以下の例では、makeAdder()は変数addByを捕まえている。戻された関数がどこに戻されようと、それは数字addByを覚えている。
|
以下はトップ・レベルの関数、staticメソッド、及びインスタンス・メソッドが同じかどうかをテストしている例である:
|
総ての関数は値を返す。もし返し値が指定されていないときは文return null;が暗示的にその関数ボディに付加される。
|
その関数の型が指定されていないときはdynamicが指定されていると考えても良い。nullもdynamic型なので、上のコードは
|
と書いても同じ効果となる。