Menu
Coddy logo textTech

レコード (Dart 3)

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 86/110。

Dart 3では、完全なクラスを作成することなく複数の値をまとめるための軽量な方法であるレコードが導入されました。レコードは不変(イミュータブル)であり、組み込みの等価性を備えています。関数から複数の値を返したり、関連するデータを一時的にグループ化したりするのに最適です。

値を括弧で囲むことでレコードを作成します:

void main() {
  // 2つの値を持つシンプルなレコード
  var point = (10, 20);
  print(point.$1);  // 10 - 最初の要素にアクセス
  print(point.$2);  // 20 - 2番目の要素にアクセス
  
  // 名前付きフィールドを持つレコード
  var person = (name: 'Alice', age: 30);
  print(person.name);  // Alice
  print(person.age);   // 30
}

位置フィールドは $1$2 などでアクセスされますが、名前付きフィールドはその名前を直接使用します。1つのレコード内で両方のスタイルを混在させることができます。

レコードは、関数から複数の値を返す際に真価を発揮します。2つのデータを返すためだけにクラスを作成する必要はありません。

(int, int) divideWithRemainder(int a, int b) {
  return (a ~/ b, a % b);
}

(String name, int age) getUser() {
  return ('Bob', 25);
}

void main() {
  var (quotient, remainder) = divideWithRemainder(17, 5);
  print('$quotient remainder $remainder');  // 3 余り 2
  
  var (name, age) = getUser();
  print('$name is $age');  // Bob は 25
}

レコードは値に基づいた等価性を自動的にサポートするため、自分で == を実装することなく簡単に比較を行うことができます。シンプルで不変なデータのグループ化が必要な場合はレコードを使用し、メソッド、可変性、またはより複雑な動作が必要な場合はクラスを使用してください。

challenge icon

チャレンジ

簡単

レコードを使って座標系を構築しましょう!レコードとして表現された2Dポイントを扱う関数を作成し、完全なクラスを作成することなく、複数の関連する値を返したり操作したりすることがレコードによっていかに簡単になるかを体験します。

コードを2つのファイルに整理します:

  • geometry.dart: 座標データを扱うためにレコードを使用する関数を作成します:
    • 名前付きフィールド xy を持つレコードを返す関数 createPoint(int x, int y)。戻り値の型は ({int x, int y}) とします。
    • 2つのポイント間の中間地点を計算し、名前付きフィールドを持つレコードとして返す関数 midpoint(({int x, int y}) p1, ({int x, int y}) p2)。計算には整数除算 (~/) を使用してください。
    • 位置指定フィールドを持つレコード(水平距離(xの差)と垂直距離(yの差))を返す関数 distance(({int x, int y}) p1, ({int x, int y}) p2)。戻り値の型は (int, int) とします。p2.x - p1.x および p2.y - p1.y として計算してください。
  • main.dart: geometryファイルをインポートし、レコードの動作を実演します:
    • createPoint を使用して2つのポイントを作成します:ポイントA (2, 4) とポイントB (10, 8)
    • 名前付きフィールドを使用して 'Point A: (${pointA.x}, ${pointA.y})' と出力します。
    • 名前付きフィールドを使用して 'Point B: (${pointB.x}, ${pointB.y})' と出力します。
    • 中間地点を計算し、'Midpoint: (${mid.x}, ${mid.y})' と出力します。
    • 距離を計算し、分割代入を使用して値を変数 dxdy に抽出し、'Distance: dx=$dx, dy=$dy' と出力します。

意味が重要な場合には名前付きフィールドで明確に、文脈が明らかな場合には位置指定フィールドを使用するなど、レコードがいかに綺麗に複数の値を返せるかに注目してください!

期待される出力:

Point A: (2, 4)
Point B: (10, 8)
Midpoint: (6, 6)
Distance: dx=8, dy=4

チートシート

Dart 3では**レコード**が導入されました。これは、完全なクラスを作成することなく、複数の値を軽量にまとめる方法です。レコードは不変(イミュータブル)であり、関数から複数の値を返すのに最適です。

値を括弧で囲むことでレコードを作成します:

// 位置指定フィールドを持つシンプルなレコード
var point = (10, 20);
print(point.$1);  // 10 - 最初の要素にアクセス
print(point.$2);  // 20 - 2番目の要素にアクセス

// 名前付きフィールドを持つレコード
var person = (name: 'Alice', age: 30);
print(person.name);  // Alice
print(person.age);   // 30

位置指定フィールドには $1$2 などでアクセスし、名前付きフィールドにはその名前を直接使用してアクセスします。

レコードは、関数から複数の値を返すのに理想的です:

// 戻り値の型でレコードの構造を指定
(int, int) divideWithRemainder(int a, int b) {
  return (a ~/ b, a % b);
}

// 戻り値の型における名前付きフィールド
(String name, int age) getUser() {
  return ('Bob', 25);
}

// レコードの非構造化(デストラクト)
var (quotient, remainder) = divideWithRemainder(17, 5);
print('$quotient remainder $remainder');  // 3 remainder 2

var (name, age) = getUser();
print('$name is $age');  // Bob is 25

名前付きフィールドを持つレコードの戻り値の型には、波括弧を使用します:

({int x, int y}) createPoint(int x, int y) {
  return (x: x, y: y);
}

レコードは、その値に基づいた等価性を自動的にサポートします。

自分で試してみよう

import 'geometry.dart';

void main() {
  // TODO: createPoint を使用して (2, 4) に点 A を作成します
  
  // TODO: createPoint を使用して (10, 8) に点 B を作成します
  
  // TODO: 名前付きフィールドを使用して点 A を出力します: Point A: ([x], [y])
  
  // TODO: 名前付きフィールドを使用して点 B を出力します: Point B: ([x], [y])
  
  // TODO: 中点を計算して出力します: Midpoint: ([x], [y])
  
  // TODO: 距離を計算し、非構造化を使用して dx と dy に抽出します
  // その後、次のように出力します: Distance: dx=[dx], dy=[dy]
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン