Dart
Dartは、オブジェクト指向のプログラミング言語で、Googleによって開発されました。Dartは、Webアプリケーションやモバイルアプリケーションの開発に適しており、柔軟性と高速性が特徴です。
このチュートリアルでは、Dartプログラミング言語の基本的な構文と機能について学ぶことができます。初心者から中級者まで、プログラミングのスキルレベルに関わらず、誰でもこのチュートリアルから学習を開始することができます。
Dart について
編集DartはGoogleによって開発されたオブジェクト指向のプログラミング言語です。Webアプリケーションやモバイルアプリケーションの開発に使用されています。
Dartは静的型付けの言語で、ライブラリやフレームワークが豊富に用意されています。FlutterというUIフレームワークもDartで書かれており、Flutterにより、多くの種類のプラットフォーム向けにアプリケーションを開発することができます。
Dartの特徴
編集- クライアント・サーバーアプリケーションのどちらにも使用できるオブジェクト指向プログラミング言語
- 標準ライブラリを備えており、HTTP、JSON、XML、WebSocket、および他のよく使用されるプロトコルをサポート
- マルチプラットフォーム対応:Webアプリ、モバイルアプリ(iOS / Android)、デスクトップアプリ(Windows / macOS / Linux)向けにプログラムを記述できる
- JIT(Just-In-Time)コンパイラによる高速な開発プロセスを提供
- AOT(Ahead-Of-Time)コンパイルにより高いパフォーマンスを発揮できる
- Flutter Frameworkの言語として採用されており、クロスプラットフォームモバイルアプリの開発に役立つ
Dartの文法的特徴
編集- オブジェクト指向:Dartは、完全にオブジェクト指向の言語です。それは、すべてがオブジェクトであるということを意味します。
- 型安全:Dartは、静的型付け言語です。つまり、コンパイル時に型エラーを検出できます。
- 非同期処理:Dartは、非同期処理に優れています。それは、コールバック関数を使用せずに非同期処理を実現できるasync/awaitキーワードを提供しています。
- ビルトイン型:Dartは、多くのビルトイン型を提供しています。例えば、int、double、String、List、Map、Setなどです。
- 変数の宣言:Dartでは、varキーワードを使用して変数を宣言できます。また、finalキーワードを使用して定数を宣言することもできます。
- ラムダ:Dartは、ラムダ式をサポートしています。それは、簡潔な構文で関数を定義することができるということを意味します。
- クラス:Dartは、クラスをサポートしています。それは、オブジェクト指向プログラミングの基本的な要素の1つです。
インストールしないで実行する方法
編集DartPad というサイトを使うと、自分の環境にインストールしないでもブラウザ上で Dart のプログラミングが行なえます。
インストール方法
編集Get the Dart SDK を入手できます。
Windowsの場合は、公式インストール方法ではパッケージマネージャーの Chocolatey を使うのですが日本では馴染みがないようなので、これとは別にコミュニティによる用意されたインストーラを使うと良いでしょう。⇒ https://gekorm.com/dart-windows/
動作確認
編集dartのバージョンは、
dart --version
で確認できます。
PS C:\Users\user1> dart --version Dart SDK version: 2.15.0-168.0.dev (dev) (Thu Sep 30 12:23:13 2021 -0700) on "windows_x64"
のようにバージョン番号が出たら、成功です。
Hello World など
編集- コード例
main() { print("Hello world"); }
print関数などのよく使われる関数はdart:core
ライブラリにありますが、dart:core
ライブラリはimport不要で使えます。
実行方法は、コマンドプロンプトあるいはWindows Terminalで、
dart ファイル名.dart
です。
Dartに特徴的な機能
編集Dartが初めてのプログラミング言語という人は少ないでしょうから、他の言語にはあまりないDart固有の機能をまとめて紹介します。
null安全性
編集Dartではnull安全性が組み込まれており、変数がnullを許容するかどうかを明示的に指定します。例えば、int
型の変数にnullを代入するためにはint?
という型を使用します。この機能により、nullに関連するエラーやバグを減らすことができます。
int? nullableInt = null; int nonNullableInt = 42;
以下は、Dart 2.12で追加されたNull安全性の機能を含んだコードの例です。Null安全性はDartの重要な変更点の一つであり、?
や!
などの新しい機能が導入されました。
void main() { String? nullableString = "Hello"; // ?を使ってnullが許容される文字列型の変数を宣言 if (nullableString != null) { print(nullableString.length); // nullチェック後のnullableStringのプロパティに安全にアクセス } else { print("String is null"); } String nonNullableString = nullableString ?? "Fallback"; // nullの場合に代替値を設定する方法 print(nonNullableString); String nonNullableString2 = nullableString!; // nullチェックをバイパスし、非nullを保証する演算子 print(nonNullableString2); }
この例では、String? nullableString
というnullableな文字列型変数を宣言し、if
文でnullチェックして安全にプロパティにアクセスしています。また、??
演算子を使用してnullableな変数がnullの場合の代替値を設定し、!
演算子を使ってnullチェックをバイパスして非nullを保証しています。
拡張メソッド
編集Dartには他の言語にはあまり見られない、拡張メソッドという機能があります。これにより、既存のクラスに新しいメソッドを追加することができます。
extension StringExtension on String { int get doubleLength => this * 2; } void main() { String text = "Hello"; print(text.doubleLength); // 出力: 10 }
コレクションの初期化
編集Dartではリストやマップ、セットなどのコレクションを簡潔に初期化するための構文があります。
// リストの初期化 List<int> numbers = [1, 2, 3, 4, 5]; // マップの初期化 Map<String, int> ages = { 'John': 30, 'Alice': 25, 'Bob': 35, }; // セットの初期化 Set<String> fruits = {'apple', 'banana', 'orange'};
これにより、コレクションを宣言して初期化する手順を簡素化できます。
コンストラクタの省略
編集クラスのコンストラクタがフィールドの初期化しか行わない場合、{this.field}
のような記述でコンストラクタを省略できます。
class Person { final String name; final int age; Person(this.name, this.age); // コンストラクタの省略 }
これにより、フィールドの初期化を簡潔に表現できます。
変数
編集Dartには整数型や浮動小数型などの型があります。変数の宣言のときに型を明示する方法と、初期化する値から型を推論する方法があります。
型を明示する方法
編集main() { int a = 3; int b = 2 * a; print(b); }
- 実行結果
6
初期化する値から型を推論する方法
編集Dartでは、型は var 宣言を使うことで型推論させることができます。
main() { var a = 4; var b = 1.23; print(a + b); }
- 実行結果
5.23
定数を宣言する場合
編集main() { final List<int> a = [1, 2, 3]; const List<int> b = [2, 3, 5]; print(a + b); a[1] = 9; print(a + b); // b[0] = 0; ← const で宣言すると指し示す先のオブジェクトの値も変更できないので、この文はエラー }
- 実行結果
[1, 2, 3, 2, 3, 5] [1, 9, 3, 2, 3, 5]
final
で宣言すると、初期化以降値を変更できない変数を作ることができます。しかし、final
で宣言された変数の参照先の値は変更できます。const
で宣言すると、初期化以降値を変更できない変数を作ることができます。また、const
で宣言された変数の参照先の値も変更できません。
文字列への式の埋込み
編集文字列の中に、式を埋め込むことができます。
- コード例
main() { var num = 4; print("num + 3 is ${num + 3}\n"); }
- 実行結果
num + 3 is 7
文字列の中に ${ 式 }
の形式で埋込むと、実行時に展開されます。
文字列への変数の埋込み
編集文字列の中に埋込む式が単一の変数ならばより簡略化した形式で表現できます。
- コード例
main() { var num = 4; print("num is $num\n"); }
- 実行結果
num is 4
文字列の中に $変数名
の形式で埋込むと、実行時に展開されます。
バックスラッシュ・エスケープ・シーケンス
編集改行などに対するバックスラッシュ・エスケープ・シーケンスは他のプログラミング言語と同様に使えます。
「\」そのものを表示したい場合は、先頭にもうひとつ「\」をつけて「\\」のようにするだけです。
- コード例
main() { print("\\n"); }
- 実行結果
\n
型
編集以下は、Dartにおける一般的な型とそれぞれのリテラル例を示した一覧表です。
Dartにおける一般的な型とそれぞれのリテラル例 型名 意味 リテラル例 int 整数型 42, -10, 0 double 浮動小数点数型 3.14, -0.5, 2.0 num 数値型(intまたはdouble) 42, 3.14 bool 真理値型 true, false String 文字列型 'Hello', "Dart", '''Multi-line String''' List リスト(配列)型 [1, 2, 3], ['apple', 'banana', 'orange'] Map マップ(連想配列)型 {'name': 'Alice', 'age': 30} Set セット(集合)型 {1, 2, 3}, {'apple', 'banana', 'orange'} Runes Unicode文字の集合 Runes('\u{1F60A}') Symbol シンボル型 #symbolName
これらはDartでよく使われる基本的な型の一覧です。それぞれの型は異なる種類のデータを表し、リテラル例はその型の値を表す例示です。
void main() { num a = 10; int b = 5; double c = 3.14; String name = "John Doe"; bool isTrue = true; List<int> numbers = [1, 2, 3, 4, 5]; Map<String, int> ages = { "John": 36, "Jane": 25, "Bob": 42, }; // 変数の値を表示 print('Value of a: $a'); print('Value of b: $b'); print('Value of c: $c'); print('Value of name: $name'); print('Value of isTrue: $isTrue'); // リストの要素を表示 print('Elements in numbers list:'); for (var number in numbers) { print(number); } // マップの要素を表示 print('Entries in ages map:'); ages.forEach((key, value) { print('$key is $value years old'); }); // 関数の実行結果を表示 int result = multiply(b, 6); print('Result of multiplication: $result'); } // 関数の定義 int multiply(int x, int y) { return x * y; }
- 実行結果
Value of a: 10 Value of b: 5 Value of c: 3.14 Value of name: John Doe Value of isTrue: true Elements in numbers list: 1 2 3 4 5 Entries in ages map: John is 36 years old Jane is 25 years old Bob is 42 years old Result of multiplication: 30
generate() メソッド
編集Dartには、コレクションの要素を特定の条件に基づいて動的に生成するためのgenerate
メソッドがあります。これはリストやマップを生成する際に便利な方法です。
List<int> myList = List.generate(5, (index) => index * 2); print(myList); // 出力: [0, 2, 4, 6, 8]
この例では、List.generate
メソッドを使用して、要素数が5個で、各要素がインデックスに2を掛けた値で初期化されたリストを生成しています。
generate
メソッドの構文は次のようになります。
List<E> List.generate<E>(int count, E Function(int index) generator, {bool growable: true})
count
: 生成する要素の数を指定します。generator
: 要素を生成するための関数です。この関数は引数として現在のインデックスを受け取り、その位置の要素を返します。growable
: リストが変更可能かどうかを指定します。デフォルトではtrue
です。
同様に、generate
メソッドはマップを生成するためにも使用できます。
Map<int, String> myMap = Map.fromEntries( List.generate(5, (index) => MapEntry(index, 'Value $index')), ); print(myMap); // 出力: {0: Value 0, 1: Value 1, 2: Value 2, 3: Value 3, 4: Value 4}
この例では、0から4までのキーを持ち、それぞれの値が'Value 0'
から'Value 4'
まで連番になるマップを生成しています。
イテレーションメソッド
編集Dartのコレクション(リスト、マップ、セットなど)を操作するために便利なイテレーションメソッドがいくつか用意されています。以下はその一部です。
リストやセットのためのメソッド
編集forEach: 各要素に対して特定の処理を行います。
List<int> numbers = [1, 2, 3, 4, 5]; numbers.forEach((number) => print(number)); // 各要素を出力
map: 各要素を変換して新しいリストを作成します。
List<int> numbers = [1, 2, 3, 4, 5]; List<int> doubled = numbers.map((number) => number * 2).toList(); // 各要素を2倍にして新しいリストを作成
where: 条件に一致する要素のみをフィルタリングします。
List<int> numbers = [1, 2, 3, 4, 5]; List<int> evenNumbers = numbers.where((number) => number % 2 == 0).toList(); // 偶数のみを抽出
reduce: 要素を結合して1つの値に縮約します。
List<int> numbers = [1, 2, 3, 4, 5]; int sum = numbers.reduce((value, element) => value + element); // 要素の合計を計算
マップのためのメソッド
編集forEach: 各エントリー(キーと値のペア)に対して特定の処理を行います。
Map<String, int> ages = {'John': 30, 'Alice': 25, 'Bob': 35}; ages.forEach((key, value) => print('$key is $value years old')); // 各エントリーを出力
map: キーと値を変換して新しいマップを作成します。
Map<String, int> ages = {'John': 30, 'Alice': 25, 'Bob': 35}; Map<String, String> greetings = ages.map((key, value) => MapEntry(key, 'Hello, $key!')); // キーに挨拶を追加した新しいマップを作成
keys / values: キーのコレクション / 値のコレクションを取得します。
Map<String, int> ages = {'John': 30, 'Alice': 25, 'Bob': 35}; Iterable<String> names = ages.keys; // キーのコレクションを取得 Iterable<int> values = ages.values; // 値のコレクションを取得
これらのイテレーションメソッドは、コレクションの要素を効果的に操作し、必要な処理を実行するために利用されます。
式と演算子
編集void main() { // 算術演算子 int num1 = 10; int num2 = 5; print('num1 + num2 = ${num1 + num2}'); // 和 print('num1 - num2 = ${num1 - num2}'); // 差 print('num1 * num2 = ${num1 * num2}'); // 積 print('num1 / num2 = ${num1 / num2}'); // 商 print('num1 % num2 = ${num1 % num2}'); // 余り // 比較演算子 int a = 10; int b = 20; print('a == b : ${a == b}'); // aとbが等しいか? print('a != b : ${a != b}'); // aとbが等しくないか? print('a > b : ${a > b}'); // aがbより大きいか? print('a < b : ${a < b}'); // aがbより小さいか? print('b >= a : ${b >= a}'); // bがa以上か? print('b <= a : ${b <= a}'); // bがa以下か? // 論理演算子 bool val1 = true; bool val2 = false; print('val1 && val2 : ${val1 && val2}'); // val1とval2が両方ともtrueか? print('val1 || val2 : ${val1 || val2}'); // val1とval2のどちらかがtrueか? print('!val1 : ${!val1}'); // val1の否定 // 代入演算子 int x = 10; print('x = $x'); // xの初期値を表示 x += 5; // xに5を加算 print('x += 5 : $x'); // 結果を表示 x -= 3; // xに3を減算 print('x -= 3 : $x'); // 結果を表示 x *= 4; // xに4を乗算 print('x *= 4 : $x'); // 結果を表示 // 条件付き演算子 int age = 20; String message = (age < 18) ? '未成年です' : '成人です'; print('message : $message'); // 結果を表示 }
Dartの演算子の優先順位 優先順位 種類 演算子 15 単項 - ! ~ ++ -- 14 乗算 * / % ~/ 13 加算 + - 12 シフト << >> 11 関係 < <= > >= 10 等価 == != 9 ビット積 & 8 ビット和 ^ 7 ビット和(OR) 6 条件(AND) && 5 条件(OR) || 4 条件式 ? ... : 3 代入 = *= /= ~/= %= += -= <<= >>= &= ^= |= 2 条件 is as 1 条件 if null
コメント
編集コメントをソースコードに書くときは、//
や、/*
と*/
を使用します。
1行で終わるコメント
編集下記コードのように//
を使うと行末までがコメントになります。
- コード例
main() { var cat = "Miku"; // 猫の名前 print("The name of the cat is ${cat} \n"); }
- 実行結果
The name of the cat is Miku
複数行に渡るコメント
編集下記コードのように/*
と*/
で文を囲むと2行以上をコメントにできます。
- コード例
main() { /* 動物の種類 猫 犬 兎 */ var animals = ["cat", "dog", "rabbit"]; print(animals); }
- 実行結果
[cat, dog, rabbit]
関数
編集関数の定義と呼出し
編集関数は戻値の型と関数名と仮引数リストで定義します。 関数の呼出しは、関数名と実引数リストを与えます。
- コード例
main() { // add関数に引数として1と2を指定する。 print(add(1, 2)); // sub関数に引数として1と2を指定する。 print(sub(1, 2)); } int add(int x, int y) { int z = x + y; return z; } int sub(int x, int y) => x - y;
- 実行結果
3 -1
このコードでは、add
関数とsub
関数が定義されています。add
関数はx
とy
の和を計算し、その結果を返します。一方、sub
関数はx
からy
を引いた結果を返します。main
関数では、これらの関数を引数を指定して呼び出し、結果を出力しています。
add
関数は通常のブロック構文で定義されており、sub
関数はアロー構文を使用して短く表記されています。このような表記法は、1行で簡潔に関数を定義する場合に便利です。
ライブラリー
編集Dartには、様々な用途に特化した多くのライブラリーが存在します。以下は、Dartでよく使用されるライブラリーの一部です。
dart:async
- Dartの非同期処理を扱うためのライブラリーdart:math
- 数学関数を提供するライブラリーdart:io
- ファイル入出力やネットワーク通信を扱うためのライブラリーdart:convert
- JSONやUTF-8などのエンコーディングやデコーディングを扱うためのライブラリーdart:html
- Webアプリケーションを開発するためのHTML要素を提供するライブラリーhttp
- HTTP通信を扱うためのライブラリーflutter
- モバイルアプリケーションを開発するためのフレームワークであり、UIやネットワーク通信などの機能を提供するライブラリー
これらのライブラリー以外にも、様々な目的に特化したライブラリーが存在しています。Dartの公式ドキュメントや、Dart Pubと呼ばれるパッケージマネージャーで検索することで、必要なライブラリーを見つけることができます。
print関数などのよく使われる関数はdart:core
ライブラリーにあり、dart:core
ライブラリーはimportが不要ですが、それ以外のライブラリーはimportが必要です。
math ライブラリー
編集- コード例
import 'dart:math'; num numval = pi; main() { print(sin(pi / 4)); }
- 実行結果
0.7071067811865475
- ライブラリーのimport
import 'dart:math';
- num型
num numval = pi;
- num型は、数値ならば整数型でも浮動小数点数型でも保持できる変数を宣言します。
- ライブラリー関数の使用
print(sin(pi / 4));
Dartの型システムの説明の前なので、簡単な例の説明に留めました。
制御構造
編集Dartには、if-else文、for文、while文、do-while文、switch文、forEachメソッドなど、多様な制御構造があります。
- Dartプログラミング言語の主要な制御構造のチートシート
void main() { // if文 int x = 10; if (x > 5) { print("x is greater than 5"); } else if (x == 5) { print("x is equal to 5"); } else { print("x is less than 5"); } // forループ for (int i = 0; i < 5; i++) { print("i is $i"); } // for-inループ List<int> numbers = [1, 2, 3, 4, 5]; for (int number in numbers) { print("number is $number"); } // whileループ int count = 0; while (count < 5) { print("count is $count"); count++; } // do-whileループ int i = 0; do { print("i is $i"); i++; } while (i < 5); // switch文 String color = "red"; switch (color) { case "red": print("color is red"); break; case "green": print("color is green"); break; case "blue": print("color is blue"); break; default: print("color is not recognized"); break; } // break文 for (int i = 0; i < 5; i++) { if (i == 3) { break; } print("i is $i"); } // continue文 for (int i = 0; i < 5; i++) { if (i == 3) { continue; } print("i is $i"); } }
- 実行結果
x is greater than 5 i is 0 i is 1 i is 2 i is 3 i is 4 number is 1 number is 2 number is 3 number is 4 number is 5 count is 0 count is 1 count is 2 count is 3 count is 4 i is 0 i is 1 i is 2 i is 3 i is 4 color is red i is 0 i is 1 i is 2 i is 4 i is 5 i is 0 i is 1 i is 2 i is 4 i is 5
for文とif文の組合わせ
編集- for文とif文の組合わせ
main() { for (int i = 0; i < 10; i++) { if (i % 2 == 0) { print("$i は2の倍数です。"); } else if (i % 3 == 0) { print("$i は3の倍数です。"); } else { print("$i は2の倍数でも3の倍数でもありません。"); } } }
- 実行結果
0 は2の倍数です。 1 は2の倍数でも3の倍数でもありません。 2 は2の倍数です。 3 は3の倍数です。 4 は2の倍数です。 5 は2の倍数でも3の倍数でもありません。 6 は2の倍数です。 7 は2の倍数でも3の倍数でもありません。 8 は2の倍数です。 9 は3の倍数です。
- for文
for (int i = 0; i < 10; i++) {
- C言語などと共通する三文式for文です。
- 最初の文は初期設定で変数の宣言もでき、宣言された変数のスコープはfor文が終わるまでです。
- 二番目の文は反復条件で、値はtrueかfalseである必要があり、trueであるかぎる続く文(内容としましょう)を反復します。
- 三番目の文は内容を実行したあと毎回評価されます。
- if文
if (i % 2 == 0) { print("$i は2の倍数です。"); } else if (i % 3 == 0) { print("$i は3の倍数です。"); } else { print("$i は2の倍数でも3の倍数でもありません。"); }
i % 2
は、「iを2で割った余り」を表す式で、条件式全体i % 2 == 0
は、「iを2で割った余りが0ならtrueそうでなければfalse」を表す式、、となります。- if文は条件式がtrueであればifに続く文を、(else節があれば)条件式がfalseならばelseに続く文を実行します。
- if の条件式に一致せずelseがある場合はelseに続く文を実行しますが、if文も文なので
} else if (i % 3 == 0) {
のように続けて別の条件を評価することができます[1]。
while文とif文の組合わせ
編集- while文とif文の組合わせ
main() { int i = 0; while (i < 10) { if (i % 2 != 0) { print("$i は奇数です。"); } else { print("$i は偶数です。"); } i++; } print(i); }
- 実行結果
0 は偶数です。 1 は奇数です。 2 は偶数です。 3 は奇数です。 4 は偶数です。 5 は奇数です。 6 は偶数です。 7 は奇数です。 8 は偶数です。 9 は奇数です。 10
- while文とスコープ
print(i);
- 上のfor文とif文の組合わせをwhileを使ってあらわあすと、概ねこのようになります。
- 「概ね」と言うのは、変数iのスコープは(while文の外で宣言しているので)main関数末まで続くので、while文を抜けても
print(i);
は有効です。
for文とswitch文の組合わせ
編集- for文とswitch文の組合わせ
main() { for (int i = 1; i < 10; i++) { switch (i) { case 2: case 4: case 6: case 8: print("$iは、2の倍数です。"); break; case 3: case 6: case 9: print("$iは、3の倍数です。"); break; case 5: print("$iは、5の倍数です。"); break; case 7: print("$iは、7の倍数です。"); break; default: print("$iは、2,3,5,7のいづれの倍数でもありません。"); } } }
- 実行結果
1は、2,3,5,7のいづれの倍数でもありません。 2は、2の倍数です。 3は、3の倍数です。 4は、2の倍数です。 5は、5の倍数です。 6は、2の倍数です。 7は、7の倍数です。 8は、2の倍数です。 9は、3の倍数です。
forEachメソッド
編集forEach
メソッドは、リストやマップなどのコレクションの各要素に対して指定した処理を実行するためのメソッドです。
- forEachメソッド
void main() { List<String> fruits = ["apple", "banana", "orange"]; // リストの各要素に対して、処理を実行する fruits.forEach((fruit) { print(fruit); }); }
- 上記の例では、
List
型のfruits
という変数に対してforEach
メソッドを呼び出し、fruits
の各要素に対して、引数で指定された無名関数を実行しています。無名関数内では、print
メソッドを使用して、各要素をコンソールに出力しています。
また、forEach
メソッドは、以下のようにラムダ式を使用して簡略化することもできます。
- forEachメソッドとラムダ式の組み合わせ
void main() { List<String> fruits = ["apple", "banana", "orange"]; // リストの各要素に対して、処理を実行する(ラムダ式) fruits.forEach((fruit) => print(fruit)); }
- 上記の例では、引数で指定する無名関数をラムダ式で表しています。これにより、コードの記述量が簡略化されます。
for文とコレクションの組合わせ
編集- for文とコレクションの組合わせ
main() { const array = [2, 3, 5, 7]; print(array.runtimeType); for (var x in array) { print("for-in: $x"); } array.forEach((item) { print("forEach: $item"); }); const set = {2, 3, 5, 7}; print(set.runtimeType); for (var x in set) { print("for-in: $x"); } set.forEach((item) { print("forEach: $item"); }); const map = {'apple': 'リンゴ', 'orange': 'オレンジ', 'banana': 'バナナ'}; print(map.runtimeType); for (var key in map.keys) { print('$key : ${map[key]}'); } for (var value in map.values) { print('for in map.values: $value'); } map.forEach((var key, var value) { print('forEach: $key : $value'); }); }
- 実行結果
JSArray<int> for-in: 2 for-in: 3 for-in: 5 for-in: 7 forEach: 2 forEach: 3 forEach: 5 forEach: 7 _UnmodifiableSet<int> for-in: 2 for-in: 3 for-in: 5 for-in: 7 forEach: 2 forEach: 3 forEach: 5 forEach: 7 ConstantStringMap<String, String> apple : リンゴ orange : オレンジ banana : バナナ for in map.values: リンゴ for in map.values: オレンジ for in map.values: バナナ forEach: apple : リンゴ forEach: orange : オレンジ forEach: banana : バナナ
オブジェクト指向プログラミング
編集Dartはオブジェクト指向言語であり、クラスを使ってオブジェクトを定義できます。クラスはフィールドとメソッドを持ち、フィールドはオブジェクトの状態を表し、メソッドはオブジェクトの振る舞いを定義します。
以下は、Dartでクラスを定義し、オブジェクトを作成する例です。例として、Personクラスを定義し、名前と年齢をフィールドに持ち、年齢を1歳増やすメソッドを定義します。また、main関数で2つのPersonオブジェクトを作成し、1つのオブジェクトの年齢を増やして表示します。
- クラスの例
// Personクラスの定義 class Person { String name; // 名前 int age; // 年齢 // コンストラクタ Person(this.name, this.age); // 年齢を1歳増やすメソッド void incrementAge() { age++; } } void main() { // Personオブジェクトの作成 var person1 = Person('Alice', 20); var person2 = Person('Bob', 25); // person1の年齢を1歳増やす person1.incrementAge(); // person1とperson2の情報を表示する print('Person 1: ${person1.name}(${person1.age})'); print('Person 2: ${person2.name}(${person2.age})'); }
- 実行結果
Person 1: Alice(21) Person 2: Bob(25)
- まず、Personクラスが定義されています。このクラスには、名前と年齢を表すString型の
name
フィールドとint型のage
フィールドが含まれています。また、このクラスには、名前と年齢を引数として受け取るコンストラクタと、年齢を1歳増やすメソッドincrementAge
が含まれています。 - 次に、
main
関数では、2つのPersonオブジェクトを作成し、1つのオブジェクトの年齢を増やしています。var
キーワードを使って、Personオブジェクトを作成しています。Person('Alice', 20)
は、Person
クラスのコンストラクタを呼び出し、name
を'Alice'、age
を20で初期化しています。 - その後、
person1
オブジェクトのincrementAge()
メソッドが呼び出され、その結果、person1
オブジェクトのage
フィールドが1増えます。 - 最後に、
person1
とperson2
の情報を表示するために、print()
関数が使用されます。それぞれのオブジェクトの名前と年齢が表示されます。
継承
編集継承は、既存のクラスを基にして新しいクラスを定義することができます。
- 継承の例
// Personクラスの定義 class Person { String name; // 名前 int age; // 年齢 // コンストラクタ Person(this.name, this.age); // 年齢を1歳増やすメソッド void incrementAge() { age++; } } void main() { // Personオブジェクトの作成 var person1 = Person('Alice', 20); var person2 = Person('Bob', 25); // person1の年齢を1歳増やす person1.incrementAge(); // person1とperson2の情報を表示する print('Person 1: ${person1.name}(${person1.age})'); print('Person 2: ${person2.name}(${person2.age})'); }
- 実行結果
Tama is eating. Meow!
- この例では、
Animal
という親クラスが定義され、name
というインスタンス変数とeat()
というメソッドが定義されています。そして、Cat
という子クラスがAnimal
を継承しています。Cat
はname
を引数に取るコンストラクタとmeow()
というメソッドを持っています。 main
関数では、Cat
のインスタンスであるcat
を作成し、eat()
とmeow()
メソッドを呼び出しています。eat()
メソッドはAnimal
クラスから継承されたもので、meow()
メソッドはCat
クラスで定義されたものです。
コード・ギャラリー
編集コードの理解はプログラミング言語の学習において重要です。 以下は、動作する簡潔なコード例をいくつかご紹介します。
エラトステネスの篩
編集エラトステネスの篩を、若干 Dart らしく書いてみました。
- エラトステネスの篩
void eratosthenes(int n) { var sieve = List.filled(n + 1, true); for (var i = 2; i < sieve.length; i++) { if (!sieve[i]) continue; print("$i "); for (var j = 2 * i; j < sieve.length; j += i) { sieve[j] = false; } } } void main() { eratosthenes(100); }
eratosthenes
関数は、指定された整数n
までの素数を見つけるために使用されます。この関数内で、sieve
と呼ばれるブール型のリストが作成されます。このリストは、インデックスが数値に対応し、その数が素数であるかどうかを示します。最初はすべての数値が素数であると仮定されます。for
ループは、2からsieve
の長さまでの範囲で回ります。このループは、素数を探すために各数値を確認します。if (!sieve[i]) continue;
は、sieve[i]
がfalse
(素数でないことを示す)場合には処理をスキップし、次の数値を調べるためにループを進めます。素数でない数値の倍数は素数ではないため、これによって非素数を除外します。- 素数である数値
i
の倍数を探すための内側のfor
ループがあります。これは、j
を2 * i
からsieve
の長さまでi
ずつ増やしながらループします。このループでは、sieve[j]
をfalse
に設定することで、j
が素数でないことを示します。 main
関数では、eratosthenes
関数を引数100で呼び出しています。これにより、100以下のすべての素数が見つけられ、それらがコンソールに出力されます。
エラトステネスの篩は、効率的な方法で素数を見つけるアルゴリズムであり、指定された範囲の素数を比較的高速に見つけることができます。
最大公約数と最小公倍数
編集最大公約数と最小公倍数を、若干 Dart らしく書いてみました。
- 最大公約数と最小公倍数
int gcd2(int m, int n) => n == 0 ? m : gcd2(n, m % n); int gcd(List<int> ints) { if (ints.isEmpty) { throw ArgumentError("List of integers cannot be empty"); } return ints.reduce(gcd2); } int lcm2(int m, int n) => m * n ~/ gcd2(m, n); int lcm(List<int> ints) { if (ints.isEmpty) { throw ArgumentError("List of integers cannot be empty"); } return ints.reduce(lcm2); } void main() { print("gcd2(30, 45) => ${gcd2(30, 45)}"); print("gcd([30, 72, 12]) => ${gcd([30, 72, 12])}"); print("lcm2(30, 72) => ${lcm2(30, 72)}"); print("lcm([30, 42, 72]) => ${lcm([30, 42, 72])}"); }
- 実行結果
gcd2(30, 45) => 15 gcd([30, 72, 12]) => 6 lcm2(30, 72) => 360 lcm([30, 42, 72]) => 2520
以下は、Dartコードの解説です。
gcd2
関数は、ユークリッドの互除法を使用して整数m
とn
の最大公約数(GCD)を再帰的に計算します。- 三項演算子
n == 0 ? m : gcd2(n, m % n)
を使用して、n
が 0 ならばm
を、そうでなければ再帰的にgcd2
を呼び出します。 gcd
関数は、与えられた整数のリストに対して最大公約数(GCD)を計算します。- リストが空であれば、
ArgumentError
をスローして空のリストは処理できないようにしています。 ints.reduce(gcd2)
を使用して、リスト内の各要素に対してgcd2
を繰り返し適用して最終的な GCD を計算します。lcm2
関数は、整数m
とn
の最小公倍数(LCM)を計算します。m * n ~/ gcd2(m, n)
を使用して、m
とn
の積を最大公約数(gcd2
)で割ってLCMを計算しています。lcm
関数は、与えられた整数のリストに対して最小公倍数(LCM)を計算します。- リストが空であれば、
ArgumentError
をスローして空のリストは処理できないようにしています。 ints.reduce(lcm2)
を使用して、リスト内の各要素に対してlcm2
を繰り返し適用して最終的な LCM を計算します。main
関数は、それぞれの関数を呼び出して結果を出力しています。- 文字列補間 (
${}
) を使用して、関数呼び出しの結果を文字列として表示しています。
二分法
編集二分法を、若干 Dart らしく書いてみました。
- 二分法
double bisection(num low_, num high_, double Function(double) f) { double low = low_.toDouble(); double high = high_.toDouble(); double x = (low + high) / 2; double fx = f(x); if ((fx.abs()) < 1.0e-10) { return x; } if (fx < 0.0) { low = x; } else { high = x; } return bisection(low, high, f); } void main() { print('${bisection(0, 3, (double x) => x - 1)}'); print('${bisection(0, 3, (double x) => x * x - 1)}'); }
- 実行結果
0.9999999999417923 1.0000000000291038
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Dart に移植しました。
このDartコードは、二分法を使用して与えられた関数の根(ゼロとなる点)を見つけます。bisection
関数は、指定された範囲内で与えられた関数f
の根を見つけます。main
関数では、いくつかの関数を用いて根を見つけるためにbisection
関数を呼び出しています。
用語集
編集- Dart: Googleによって開発されたオープンソースのプログラミング言語。
- クラス(class): オブジェクト指向プログラミングにおけるデータとそれを操作するメソッドをまとめたもの。
- インスタンス(instance): クラスから生成された実体。
- 継承(inheritance): 親クラスのメソッドや変数を子クラスが引き継いで使用すること。
- インターフェイス(interface): クラスが実装しなければならないメソッドや変数の一覧。
- ライブラリ(library): Dartのコードをパッケージ化して再利用可能にする仕組み。
- 静的型付け(static typing): 変数にどのような型の値しか代入できないかを事前に指定すること。Dartは静的型付けに対応している。
- ミックスイン(mixin): 複数のクラスに同じメソッドや変数を追加することができる仕組み。
- Future: 非同期処理を表すオブジェクト。Dartでは、非同期処理をFutureで表現することが一般的である。
- async/await: 非同期処理をシンプルに扱うための仕組み。Dartでは、async/awaitキーワードを使用することで非同期処理を直感的に実装することができる。
- Flutter: Dartを使用したGoogleによるモバイルアプリケーションフレームワーク。
脚註
編集- ^ 他の言語では、elseifやelsifなどの特別な表現を用意している場合がありますが、Dartは「else節に対応する文がif文」と表現します。