Flutterアプリケーション開発概論

【次章への接続】データ構造へ

この節の目的は、ここまで学んだ 変数・型・条件分岐・ループ・関数 が、なぜ次の「データ構造」へつながるのかを理解することです。

第2章の前半では「どう書くか」を学びましたが、次章では「どの形でデータを持つか」を学びます。

Dart 公式でも、言語の基本を学んだあとに collections や classes などの話へ進める流れが示されており、ListSetMap、records、classes は、データを整理して扱うための重要な道具として位置づけられています。

歴史的に見ると、「データをどう表すか」はプログラミングの中心課題でした。1945年の stored-program concept では、John von Neumann によって、命令とデータを同じ記憶装置へ置く 現代計算機の基本的な考え方が広まりました。

さらに 1974 年には Barbara Liskov と Stephen Zilles が abstract data types 抽象データ型 を提案し、データを「中身の実装」ではなく「使える操作」で考える見方を強めました。

1976 年の Niklaus Wirth の本 Algorithms + Data Structures = Programs は、その関係を題名そのもので表しています。つまり、次章で学ぶデータ構造は、単なる入れ物の種類ではなく、プログラムの考え方そのものにつながるテーマです。

1. なぜ次が「データ構造」なのか

ここまでの節では、値を一つずつ扱う練習が多くありました。たとえば、点数を一つ受け取る、平均点を一つ返す、条件で一つの判定を返す、という形です。けれど、現実のアプリでは一つだけの値を扱う場面はそれほど多くありません。

実際には、複数の生徒名、複数の点数、商品の一覧、予定表、設定情報のまとまり のように、複数の値をまとめて扱う必要があります。Dart でもそのために ListMap などの collection が用意されています。

たとえば、これまで学んだコードでは「3 教科の点数をまとめて持つ」ときに List<int> を使いました。

これはすでに、データ構造の入口へ片足を入れていたということです。次章では、この「まとめて持つ」感覚をもっとはっきり整理します。

つまり、文法の学習が終わったからデータ構造へ進むのではなく、文法を使って複数データを扱う必要があるからデータ構造へ進むのです。

2. 「変数一つ」では足りなくなる瞬間

初心者の最初のコードは、だいたい一つの変数から始まります。

void main() {
  int score = 80;
  print(score);
}

これなら分かりやすいです。けれど、教科が3つになると少し変わります。

void main() {
  int math = 80;
  int english = 72;
  int science = 91;

  print(math);
  print(english);
  print(science);
}

このコードでも動きます。ただ、教科が 10 個、20 個と増えると、だんだん扱いづらくなります。「同じ種類のデータが何個もある」ときは、一つずつバラバラに持つより、まとまりとして持つほうが自然です。

そこで使うのがデータ構造です。Dart の collections では、List は順番つきの集まり、Map はキーと値の対応表として扱われます。

たとえば、点数一覧をひとまとまりにするとこうなります。

void main() {
  List<int> scores = [80, 72, 91];
  print(scores);
}

これだけで、「同じ種類の値をまとめて持つ」感覚がかなり変わります。次章は、この感覚を本格的に言語化していく章です。

3. ループとデータ構造は、実はセットで考える

2-4 で forwhile を学んだとき、ループは「同じ処理をくり返す仕組み」だと説明しました。ただ、本当に便利になるのは、くり返しの相手が複数データのまとまりであるとき です。

たとえば、名前を 3 つ表示するなら、こう書けます。

void main() {
  List<String> names = ['Aoi', 'Rin', 'Sora'];

  for (String name in names) {
    print(name);
  }
}

このコードでは、ループと List が一緒に働いています。for だけでは相手が必要ですし、List だけでは中身を順番に処理できません。つまり、ループはデータ構造を動かす道具であり、データ構造はループが活きる土台です。Dart 公式でも、Iterable と loop を組み合わせる使い方が基本的な形として説明されています。

この見方が持てると、次章で ListMap を学ぶ意味がかなり見えやすくなります。

4. 関数とデータ構造も、実は深くつながっている

第2章では、関数と戻り値を学びました。ここでも、次章へのつながりがあります。関数は、一つの値だけを受け取るとは限りません。実際には、複数のデータをひとまとまりで受け取ったり、まとめて返したりします。

たとえば、点数一覧から合計点を返す関数は、次のように書けます。

int calculateTotal(List<int> scores) {
  int total = 0;

  for (int score in scores) {
    total += score;
  }

  return total;
}

void main() {
  print(calculateTotal([80, 72, 91]));
}

ここで List<int> は、ただの型ではありません。「複数の整数をまとめて渡す」ための設計です。関数が本当に便利になるのは、こうしてデータ構造と結びついたときです。次章では、この「まとめ方」の選び方を学びます。

5. 抽象データ型という考え方

ここで、次章の背景になる歴史的な考え方を一つだけ紹介します。1974年に Barbara Liskov と Stephen Zilles は、abstract data types 抽象データ型 という考え方を提案しました。

これは、「データをどうやって中で保存しているか」よりも、「そのデータに対してどんな操作ができるか」を大切にする考え方です。

検索結果の要約でも、この論文は built-in abstractions に新しい data abstraction を追加できるアプローチとして説明されています。

初心者向けに言い換えると、たとえば「名簿」というものを考えるとき、

  • 中でどう並んでいるか
  • 何番目へアクセスできるか
  • 名前を追加できるか
  • 削除できるか

というように、「使える操作」で考えるということです。次章で学ぶ ListMap も、まさにそうです。中の仕組みを全部知らなくても、使い方を学べば役立てられます。

6. Wirth の「Algorithms + Data Structures = Programs」

1976年に Niklaus Wirth は Algorithms + Data Structures = Programs という本を出しました。題名だけ見ると数式のようですが、意味はとても深いです。

良いプログラムは、手順 algorithm だけでも、データ構造だけでも不十分で、その両方がそろって初めて成り立つという考え方です。

Internet Archive の書誌情報でも、この本は 1976 年刊で、data structures と computer algorithms を主題としていることが確認できます。

この第2章をふり返ると、まさにそうです。

  • 条件分岐やループや関数は algorithm 側の話
  • ListMap や records や classes は data structures 側の話

つまり、次章は新しい話へ飛ぶのではなく、今まで学んだ手順の学習を、データの学習で完成させる段階だと考えると分かりやすいです。

7. Dart で次に学ぶデータ構造の入口

Dart 公式の言語ドキュメントでは、collections と classes / objects が独立した大きな学習項目として並んでいます。これは、Dart ではデータの持ち方をかなり大切にしていることを示しています。とくに初心者が最初に押さえたいのは、次の四つです。

  • List 順番つきで並べるデータ構造
  • Map キーと値を対応づけるデータ構造
  • records 複数の値を一まとまりにして返しやすい仕組み
  • class 自分で新しいデータの型を作る仕組み 次章では、おそらく「どんなときに List を使い、どんなときに Map を使うのか」「なぜ class が必要になるのか」という話へ入っていきます。ここまで来ると、ただの文法学習から「設計」へ一歩進む感じが出てきます。

8. データ構造が必要な理由

ここで、同じ情報を二通りで持つ例を比べてみます。

バラバラに持つ場合

void main() {
  String student1 = 'Aoi';
  String student2 = 'Rin';
  String student3 = 'Sora';

  print(student1);
  print(student2);
  print(student3);
}

まとめて持つ場合

void main() {
  List<String> students = ['Aoi', 'Rin', 'Sora'];

  for (String student in students) {
    print(student);
  }
}

後者のほうが、人数が増えても扱いやすそうだと感じるはずです。これが、次章で本格的に学ぶ「データ構造の価値」です。文法を覚えるだけではコードは書けませんが、データを良い形で持てるようになると、文法が急に活きてきます

9. 練習問題

問題1

次章で「データ構造」を学ぶ理由を、初心者向けに一文で説明してください。

問題2

次の二つのうち、複数の同じ種類の値をまとめて持つのに自然なのはどちらですか。

  1. int score1 = 80; int score2 = 72; int score3 = 91;
  2. List<int> scores = [80, 72, 91];

問題3

次のコードは何を表示しますか。

void main() {
  List<String> names = ['Aoi', 'Rin', 'Sora'];

  for (String name in names) {
    print(name);
  }
}

問題4

「点数一覧を受け取り、合計を返す関数」で、引数の型として自然なのは何ですか。

  1. int
  2. List<int>
  3. String

問題5

Barbara Liskov と Stephen Zilles が 1974 年に強めた考え方として適切なのはどれですか。

  1. ループ文
  2. 抽象データ型
  3. null safety

問題6

Niklaus Wirth の Algorithms + Data Structures = Programs という題名は、どんなことを伝えようとしていると思いますか。短く書いてください。

10. 練習問題の答え

答え1

例です。

「一つずつの値ではなく、複数のデータをまとめて扱うために、データ構造を学ぶ必要があります。」

答え2

正解は 2 です。 同じ種類の点数をまとめるなら List<int> のほうが自然です。

答え3

AoiRinSora を順番に表示します。for-inList の中身を一つずつ取り出しているからです。

答え4

正解は **2. **List<int> です。 点数一覧は複数の整数なので、List<int> が自然です。

答え5

正解は 2. 抽象データ型 です。

1974 年の Liskov と Zilles の論文は、built-in abstractions に新しい data abstraction を追加できる考え方を扱っています。

答え6

例です。

「良いプログラムは、手順だけでなく、データの持ち方も一緒に考えないと作れない、という意味です。」

11. まとめ

この節では、なぜ次章が「データ構造」なのかを整理しました。ここまで学んだ条件分岐、ループ、関数は、どれも大切です。ただし、それらが本当に活きるのは、複数のデータを良い形で持てるようになってからです。Dart でも ListMap、records、classes などが大きな学習項目として並んでおり、データ構造は文法の先にある自然な次の一歩です。

歴史的には、1945 年の stored-program concept が「データを記憶して扱う」計算機の土台を作り、1974 年の抽象データ型が「操作できるまとまりとしてデータを考える」視点を強め、1976 年の Wirth が「アルゴリズムとデータ構造は一体だ」と明快に言語化しました。だから、次章でデータ構造を学ぶのは、単なる新出単語の追加ではなく、プログラムを組み立てる視点を一段深くするためだと言えます。

参考文献

  • Dart overview
  • Collections
  • Classes
  • Stored-program computer(Britannica)
  • Programming with abstract data types(Barbara Liskov, Stephen Zilles, 1974)
  • Algorithms + data structures = programs(Niklaus Wirth, 1976)
教材トップへ戻る