メインページ > 工学 > 情報技術 > プログラミング > Dart

Dartは、オブジェクト指向のプログラミング言語で、Googleによって開発されました。Dartは、Webアプリケーションやモバイルアプリケーションの開発に適しており、柔軟性と高速性が特徴です。

Wikipedia
Wikipedia
ウィキペディアDartの記事があります。

このチュートリアルでは、Dartプログラミング言語の基本的な構文と機能について学ぶことができます。初心者から中級者まで、プログラミングのスキルレベルに関わらず、誰でもこのチュートリアルから学習を開始することができます。

Dart について 編集

DartはGoogleによって開発されたオブジェクト指向のプログラミング言語です。Webアプリケーションやモバイルアプリケーションの開発に使用されています。

Dartは静的型付けの言語で、ライブラリやフレームワークが豊富に用意されています。FlutterというUIフレームワークもDartで書かれており、Flutterにより、多くの種類のプラットフォーム向けにアプリケーションを開発することができます。

Dartの特徴 編集

  1. クライアント・サーバーアプリケーションのどちらにも使用できるオブジェクト指向プログラミング言語
  2. 標準ライブラリを備えており、HTTP、JSON、XML、WebSocket、および他のよく使用されるプロトコルをサポート
  3. マルチプラットフォーム対応:Webアプリ、モバイルアプリ(iOS / Android)、デスクトップアプリ(Windows / macOS / Linux)向けにプログラムを記述できる
  4. JIT(Just-In-Time)コンパイラによる高速な開発プロセスを提供
  5. AOT(Ahead-Of-Time)コンパイルにより高いパフォーマンスを発揮できる
  6. Flutter Frameworkの言語として採用されており、クロスプラットフォームモバイルアプリの開発に役立つ

Dartの文法的特徴 編集

  1. オブジェクト指向:Dartは、完全にオブジェクト指向の言語です。それは、すべてがオブジェクトであるということを意味します。
  2. 型安全:Dartは、静的型付け言語です。つまり、コンパイル時に型エラーを検出できます。
  3. 非同期処理:Dartは、非同期処理に優れています。それは、コールバック関数を使用せずに非同期処理を実現できるasync/awaitキーワードを提供しています。
  4. ビルトイン型:Dartは、多くのビルトイン型を提供しています。例えば、int、double、String、List、Map、Setなどです。
  5. 変数の宣言:Dartでは、varキーワードを使用して変数を宣言できます。また、finalキーワードを使用して定数を宣言することもできます。
  6. ラムダ:Dartは、ラムダ式をサポートしています。それは、簡潔な構文で関数を定義することができるということを意味します。
  7. クラス:Dartは、クラスをサポートしています。それは、オブジェクト指向プログラミングの基本的な要素の1つです。

インストールしないで実行する方法 編集

DartPad というサイトを使うと、自分の環境にインストールしないでもブラウザ上で Dart のプログラミングが行なえます。

https://dart.dev/#try-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関数はxyの和を計算し、その結果を返します。一方、sub関数はxからyを引いた結果を返します。main関数では、これらの関数を引数を指定して呼び出し、結果を出力しています。

add関数は通常のブロック構文で定義されており、sub関数はアロー構文を使用して短く表記されています。このような表記法は、1行で簡潔に関数を定義する場合に便利です。

ライブラリー 編集

Dartには、様々な用途に特化した多くのライブラリーが存在します。以下は、Dartでよく使用されるライブラリーの一部です。

  1. dart:async - Dartの非同期処理を扱うためのライブラリー
  2. dart:math - 数学関数を提供するライブラリー
  3. dart:io - ファイル入出力やネットワーク通信を扱うためのライブラリー
  4. dart:convert - JSONやUTF-8などのエンコーディングやデコーディングを扱うためのライブラリー
  5. dart:html - Webアプリケーションを開発するためのHTML要素を提供するライブラリー
  6. http - HTTP通信を扱うためのライブラリー
  7. 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増えます。
最後に、person1person2の情報を表示するために、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を継承しています。Catnameを引数に取るコンストラクタと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);
}
  1. eratosthenes関数は、指定された整数nまでの素数を見つけるために使用されます。この関数内で、sieveと呼ばれるブール型のリストが作成されます。このリストは、インデックスが数値に対応し、その数が素数であるかどうかを示します。最初はすべての数値が素数であると仮定されます。
  2. forループは、2からsieveの長さまでの範囲で回ります。このループは、素数を探すために各数値を確認します。
  3. if (!sieve[i]) continue;は、sieve[i]false(素数でないことを示す)場合には処理をスキップし、次の数値を調べるためにループを進めます。素数でない数値の倍数は素数ではないため、これによって非素数を除外します。
  4. 素数である数値iの倍数を探すための内側のforループがあります。これは、j2 * iからsieveの長さまでiずつ増やしながらループします。このループでは、sieve[j]falseに設定することで、jが素数でないことを示します。
  5. 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関数は、ユークリッドの互除法を使用して整数 mn の最大公約数(GCD)を再帰的に計算します。
  • 三項演算子 n == 0 ? m : gcd2(n, m % n) を使用して、n が 0 ならば m を、そうでなければ再帰的に gcd2 を呼び出します。
  • gcd関数は、与えられた整数のリストに対して最大公約数(GCD)を計算します。
  • リストが空であれば、ArgumentErrorをスローして空のリストは処理できないようにしています。
  • ints.reduce(gcd2) を使用して、リスト内の各要素に対して gcd2 を繰り返し適用して最終的な GCD を計算します。
  • lcm2関数は、整数 mn の最小公倍数(LCM)を計算します。
  • m * n ~/ gcd2(m, n) を使用して、mn の積を最大公約数(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によるモバイルアプリケーションフレームワーク。

脚註 編集

  1. ^ 他の言語では、elseifやelsifなどの特別な表現を用意している場合がありますが、Dartは「else節に対応する文がif文」と表現します。

外部リンク 編集