Roadmap.shで学ぶFlutter - Advanced Dart - [Lambdas]

# 導入

[roadmap.shのFlutter](https://roadmap.sh/flutter)のAdvanced Dartの章`Lambdas`についてです。
ラムダ式(Lambdas)は、シンプルかつ直感的に関数を定義する手段を提供し、コードの可読性と効率性を向上させてくれます。

# Lambda
Lambdasとは、匿名関数とも呼ばれるもので、DartやFlutterの基本的な概念の一つです。
アロー関数(Arrow Functions)と呼ばれることもあります。
これは、短くて簡潔な関数をインラインで作成し、それを他の関数に引数として渡したり、変数に代入したりする方法です。

以下は、DartやFlutterにおけるラムダ式の一般的な使用例です。

- **イベントハンドラー**: ボタンなどのウィジェットのイベントハンドラーとしてラムダ式を使用できます
- **コールバック**: 後で実行される関数に渡すコールバックとしてラムダ式を使用できます
- **ストリーム**: ストリーム内のイベントを処理するためにラムダ式を使用できます
- **フィルタリング**: `where`や`firstWhere`といった関数を使ってコレクション内のデータをフィルタリングする際にラムダ式を使用できます

ラムダ式は`=>`演算子を使用して定義され、引数を0個以上取ることができます。
また、ラムダ式は式、ステートメント、および戻り値を含むことができます。

```dart
ReturnType functionName(ParameterType parameter) => expression;
```

この構文では、`return`文を明示的に書く必要はありません。

```dart
int ShowSum(int numOne, int numTwo) => numOne + numTwo;

void main() {
  print(ShowSum(10, 20));
  // 出力 => 30
}
```

## イベントハンドラー

Flutterでは、ラムダ式を使用してイベントハンドラーを簡単に定義することができます。
イベントハンドラーとは、ボタンのタップや入力フィールドの変更など、ユーザーの操作に応じて特定の処理を実行するための関数です。
ラムダ式を使うことで、短く簡潔なコードでイベントハンドラーを定義することができます。

以下は、`ElevatedButton`ウィジェットの`onPressed`プロパティにラムダ式を使用して、ボタンがタップされたときにコンソールにメッセージを出力する例です。

```dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Lambda Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () => print('Button pressed!'),
            child: Text('Press me'),
          ),
        ),
      ),
    );
  }
}
```


上記の例では、`onPressed`プロパティにラムダ式を使用して、ボタンが押されたときに`print`関数を呼び出しています。ラムダ式の形式は以下の通りです。

```dart
() => print('Button pressed!');
```

- `()` は無引数のラムダ式であることを示しています。
- `print('Button pressed!')` は、ボタンが押されたときに実行される処理です。

このように、Flutterではラムダ式を使って、イベントハンドラーをシンプルに書くことができます。
イベントごとに別途関数を定義する必要がなく、必要な場所で直接ラムダ式を使用することで、コードの見通しが良くなります。

## コールバック

コールバックは、特定のイベントが発生したときや、他の関数が完了したときに実行される関数です。
DartやFlutterでは、ラムダ式を使ってコールバックを簡潔に定義することができます。これにより、コードがより読みやすく、シンプルになります。

以下は、`Future`を使った非同期操作にラムダ式をコールバックとして使用する例です。

```dart
import 'dart:async';

void main() {
  fetchData().then((data) => print('Data received: $data'));
}

Future<String> fetchData() async {
  // 2秒後にデータを返す
  return await Future.delayed(Duration(seconds: 2), () => 'Sample Data');
}
```

上記の例では、`fetchData`という非同期関数が`Future<String>`を返します。`fetchData`が完了した後、その結果を受け取って何らかの処理を行うために、`then`メソッドにラムダ式をコールバックとして渡しています。

```dart
(data) => print('Data received: $data')
```

- `(data)` は、ラムダ式が`fetchData`から受け取る引数です。この場合、データは`String`型です。
- `print('Data received: $data')` は、データを受け取った後に実行される処理です。

このコードの実行結果は、`fetchData`関数が2秒後に完了し、その結果である「Sample Data」が`print`関数を通してコンソールに出力されるというものです。

ラムダ式を使うことで、コールバックをコンパクトに記述でき、非同期処理やイベント駆動型のプログラムにおいて特に便利です。

## ストリーム

Dartのストリーム(Stream)は、非同期データのシーケンスを処理するための強力なツールです。
ストリームは複数のデータイベントを順次受け取り、それに対してリアルタイムで処理を行うことができます。
ラムダ式は、このストリームの各イベントに対して、簡潔に処理を記述するために非常に役立ちます。

以下は、`Stream`を使って数値のストリームを生成し、その各イベントに対してラムダ式を使って処理を行う例です。

```dart
import 'dart:async';

void main() {
  // 数値のストリームを作成
  Stream<int> numberStream = Stream<int>.periodic(
    Duration(seconds: 1),
    (count) => count + 1,
  ).take(5); // 最初の5つの数値のみ取得

  // ストリーム内の各イベントに対してラムダ式を使用して処理
  numberStream.listen((number) => print('Received: $number'));
}
```

この例では、`Stream<int>`が1秒ごとに数値を生成し、5回まで数値を生成した後に終了します。
そのストリームの各イベントに対して、ラムダ式を使って`listen`メソッド内で処理を定義しています。

```dart
(number) => print('Received: $number')
```

- `(number)` は、ストリームから渡されるデータ（ここでは整数）を受け取るための引数です。
- `print('Received: $number')` は、ストリームからのデータを受け取り、そのデータをコンソールに出力する処理です。

このコードを実行すると、1秒ごとに「Received: 1」「Received: 2」…と順に出力され、最終的に5回目の出力で終了します。

ラムダ式を使用することで、ストリーム内のイベントに対する処理をシンプルに記述でき、リアクティブプログラミングのコーディングが容易になります。

## フィルタリング

Dartでは、コレクション（リスト、セット、マップなど）のデータを操作するための便利なメソッドが提供されています。
`where`や`firstWhere`といったメソッドを使うと、コレクション内のデータを条件に基づいてフィルタリングできます。
このとき、ラムダ式を使用して、フィルタリングの条件を簡潔に指定することができます。

### `where`によるフィルタリング
以下は、整数のリストから偶数のみを抽出する例です。

```dart
void main() {
  List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  // 偶数をフィルタリングする
  Iterable<int> evenNumbers = numbers.where((number) => number.isEven);

  print('Even numbers: $evenNumbers');
}
```

`where`メソッドは、コレクション内の各要素を順番にチェックし、指定された条件を満たすすべての要素を含む新しいコレクションを返します。

```dart
(numbers) => number.isEven
```

- `(number)` はリストの各要素を受け取る引数です。
- `number.isEven` は、偶数かどうかをチェックする条件です。

この例では、`where`メソッドを使って、`isEven`が`true`となる（つまり偶数である）要素のみをフィルタリングしています。
### `firstWhere`によるフィルタリング

以下は、リスト内で最初に条件を満たす要素を見つける例です。

```dart
void main() {
  List<String> names = ['Alice', 'Bob', 'Charlie', 'David'];

  // 'C'で始まる最初の名前を見つける
  String nameStartingWithC = names.firstWhere((name) => name.startsWith('C'));

  print('First name starting with C: $nameStartingWithC');
}
```

`firstWhere`メソッドは、コレクション内の最初の要素を返し、その要素が指定された条件を満たしている場合のみ返します。

```dart
(name) => name.startsWith('C')
```

- `(name)` はリストの各要素を受け取る引数です。
- `name.startsWith('C')` は、名前が`C`で始まるかどうかをチェックする条件です。

この例では、`firstWhere`メソッドを使って、名前が`C`で始まる最初の要素（`Charlie`）を見つけ出し、それを返します。

ラムダ式を使用することで、コレクションのフィルタリング条件を簡潔に記述でき、コードの可読性が向上します。特に、`where`や`firstWhere`といった関数を使うときに、ラムダ式を使用すると、条件の指定が非常にシンプルになります。

# 終わりに

ラムダ式は、DartやFlutterのコードをよりシンプルで理解しやすくするための強力なツールです。イベントハンドラーやコールバック、ストリームの処理、コレクションのフィルタリングなど、さまざまな場面でその真価を発揮します。日々の開発でラムダ式を活用してみようと思います。

参考：https://medium.com/jay-tillu/lambda-functions-in-dart-7db8b759f07a