コンテンツにスキップ

関数型プログラミング (Functional Programming)

関数型プログラミングはオブジェクト指向プログラミングにありがちな状態を持たない(同じ入力をすればいつも同じ結果が返ってくる)。

じゃあ状態管理はどうするのか

ずばり、オブジェクト指向プログラミングなど他のパラダイムを使う。

何でもかんでもオブジェクト指向プログラミングにこだわるべきでないのと同様に、何でもかんでも関数型プログラミングにこだわるべきでない。

現代的なプログラミング言語は関数型プログラミングとオブジェクト指向プログラミングの両方をサポートしているので、いいとこ取りをすればよいのだ。

高階関数

高階関数を使うと for ループを使った手続きから開放され、自然言語的なコーディングが可能になる。

every

Array.prototype.every() - JavaScript | MDN

every() メソッドは、列内のすべての要素が指定された関数で実装されたテストに合格するかどうかをテストします。これは論理値を返します。

JavaScript
1
2
3
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = values.every((v) => v >= 0);
console.log(result); // true
Python
1
2
3
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
result = all(v >= 0 for v in values)
print(result) # True
C#
1
2
3
4
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = values.All(v => v >= 0);
// var result = Array.TrueForAll(values, v => v >= 0);  // こういう書き方もできる
Console.WriteLine(result); // True
Kotlin
1
2
3
4
5
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    val result = values.all { it >= 0 }
    println(result) // true
}
Dart
1
2
3
4
5
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  final result = values.every((v) => v >= 0);
  print(result); // true
}

filter

Array.prototype.filter() - JavaScript | MDN

filter() メソッドは、この配列の中から、提供された関数で実装されたテストに合格した要素のみを抽出したシャローコピーを作成します。

JavaScript
1
2
3
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const odds = values.filter((v) => v % 2 === 0);
console.log(odds); // [0, 2, 4, 6, 8]
Python
1
2
3
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odds = list(filter(lambda v: v % 2 == 0, values))
print(odds) # [0, 2, 4, 6, 8]
C#
1
2
3
4
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var odds = values.Where(v => v % 2 == 0).ToArray();
// var odds = Array.FindAll(values, v => v % 2 == 0); // こういう書き方もできる
Console.WriteLine(string.Join(",", odds));  // 0,2,4,6,8
Kotlin
1
2
3
4
5
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    val odds = values.filter { it % 2 == 0 }
    println(odds.joinToString(",")) // 0,2,4,6,8
}
Dart
1
2
3
4
5
6
7
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  final result = values
      .where((v) => v % 2 == 0)
      .toList(); // toList()しないとIterableという型で返ってくる
  print(result); // [0, 2, 4, 6, 8]
}

find

Array.prototype.find() - JavaScript | MDN

find() メソッドは、提供されたテスト関数を満たす配列内の最初の要素を返します。テスト関数を満たす値がない場合は、 undefined を返します。

JavaScript
1
2
3
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const zero = values.find((v) => v === 0);
console.log(zero); // 0
Python
1
2
3
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
zero = next(filter(lambda v: v == 0, values))
print(zero) # 0
C#
1
2
3
4
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var zero = values.FirstOrDefault(v => v == 0);
// var zero = Array.Find(values, v => v == 0); // こういう書き方もできる
Console.WriteLine(zero); // 0
Kotlin
1
2
3
4
5
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    val zero = values.find { it == 0 }
    println(zero) // 0
}
Dart
1
2
3
4
5
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  final result = values.firstWhere((v) => v == 0);
  print(result); // 0
}

map

Array.prototype.map() - JavaScript | MDN

map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します

JavaScript
1
2
3
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const times = values.map((v) => v * 2);
console.log(times); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Python
1
2
3
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
times = list(map(lambda v: v * 2, values))
print(times)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
C#
1
2
3
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var times = values.Select(v => v * 2).ToArray();
Console.WriteLine(string.Join(",", times));  // 0,2,4,6,8,10,12,14,16,18
Kotlin
1
2
3
4
5
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    val times = values.map { it * 2 }
    println(times.joinToString(","))  // 0,2,4,6,8,10,12,14,16,18
}
Dart
1
2
3
4
5
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  final result = values.map((v) => v * 2).toList();
  print(result); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
}

reduce

Array.prototype.reduce() - JavaScript | MDN

reduce() メソッドは、配列のそれぞれの要素に対して、ユーザーが提供した「縮小」コールバック関数を呼び出します。その際、直前の要素における計算結果の返値を渡します。配列のすべての要素に対して縮小関数を実行した結果が単一の値が最終結果になります。

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// 合計値
const sum = values.reduce(
  (accumulator, currentValue) => accumulator + currentValue
);
console.log(sum); // 45

// 最小値
const min = values.reduce((accumulator, currentValue) =>
  accumulator > currentValue ? currentValue : accumulator
);
console.log(min); // 0

// 最大値
const max = values.reduce((accumulator, currentValue) =>
  accumulator < currentValue ? currentValue : accumulator
);
console.log(max); // 9
Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from functools import reduce
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 合計値
sum = reduce(lambda x, y: x + y, values)
print(sum)  # 45

# 最小値
min = reduce(lambda x, y: x if x < y else y, values)
print(min)  # 0

# 最大値
max = reduce(lambda x, y: x if x > y else y, values)
print(max)  # 9
C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// 合計値
var sum = values.Aggregate((x, y) => x + y);
Console.WriteLine(sum);  // 45

// 最小値
var min = values.Aggregate((x, y) => x < y ? x : y);
Console.WriteLine(min);  // 0

// 最大値
var max = values.Aggregate((x, y) => x > y ? x : y);
Console.WriteLine(max);  // 9
Kotlin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

    // 合計値
    val sum = values.fold(0) { total, accumulator -> total + accumulator }
    println(sum)  // 45

    // 最小値
    val min = values.fold(Int.MAX_VALUE) { total, accumulator -> if (accumulator < total) accumulator else total }
    println(min)  // 0

    // 最大値
    val max = values.fold(Int.MIN_VALUE) { total, accumulator -> if (accumulator > total) accumulator else total }
    println(max)  // 9
}
Dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  // 合計値
  final sum =
      values.reduce((accumulator, currentValue) => accumulator + currentValue);
  print(sum); // 45

  // 最小値
  final min = values.reduce((accumulator, currentValue) =>
      accumulator < currentValue ? accumulator : currentValue);
  print(min); // 0

  // 最大値
  final max = values.reduce((accumulator, currentValue) =>
      accumulator > currentValue ? accumulator : currentValue);
  print(max); // 9
}

some

Array.prototype.some() - JavaScript | MDN

some() メソッドは、指定された関数で実装されているテストに、配列の中の少なくとも 1 つの要素が 合格するかどうかを判定します。配列の中で指定された関数が true を返す要素を見つけた場合は true を返し、そうでない場合は false を返します。それ以外の場合は false を返します。配列は変更しません。

JavaScript
1
2
3
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const hasFour = values.some((v) => v === 4);
console.log(hasFour); // true
Python
1
2
3
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
has_four = any(v == 4 for v in values)
print(has_four)  # True
C#
1
2
3
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var hasFour = values.Any(v => v == 4);
Console.WriteLine(hasFour);  // True
Kotlin
1
2
3
4
5
6
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

    val hasFour = values.any { it == 4 }
    println(hasFour)  // true
}
Dart
1
2
3
4
5
6
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  final hasFour = values.any((v) => v == 4);
  print(hasFour); // true
}

find()some() の使い分けは以下が参考になりそう。

Javascript の配列操作: some と find と filter が便利 | 二代目俺のメモ

toSorted

ここに記載するのはすべて非破壊的にソートする方法である。

JavaScript
1
2
3
4
5
6
7
8
9
const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// 昇順
const asc = values.toSorted((x, y) => x - y);
console.log(asc);

// 降順
const desc = asc.toReversed(asc);
console.log(desc);
Python
1
2
3
4
5
6
7
8
9
values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 昇順
asc = sorted(values, key=lambda x: x)
print(asc)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 降順
desc = sorted(asc, reverse=True)
print(desc)  # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
C#
1
2
3
4
5
6
7
8
9
int[] values = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// 昇順
var asc = values.OrderBy(x => x);
Console.WriteLine(string.Join(",", asc));   // 0,1,2,3,4,5,6,7,8,9

// 降順
var desc = asc.OrderByDescending(x => x);
Console.WriteLine(string.Join(",", desc));  // 9,8,7,6,5,4,3,2,1,0
Kotlin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fun main() {
    val values = arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

    // 昇順
    val asc = values.sortedBy { it }
    println(asc.joinToString(","))

    // 降順
    val desc = asc.sortedByDescending { it }
    println(desc.joinToString(","))
}
Dart
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void main() {
  List<int> values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  // 昇順
  // sort()は破壊的なソートかつ戻り値を返さないメソッドなので
  // スプレッド演算子でコピーして、カスケード演算子でインスタンスを取得する
  final asc = [...values]..sort((x, y) => x - y);
  print(asc);

  // 降順
  final desc = [...values]..sort((x, y) => y - x);
  print(desc);
}

forEach

Array.prototype.forEach() - JavaScript | MDN

forEach() メソッドは、与えられた関数を、配列の各要素に対して一度ずつ実行します。

JavaScript
1
2
3
4
var items = ["a", "b", "c"];
items.forEach((item, index) => {
  console.log(`${index}, ${item}`);
});
Python
1
2
3
4
5
# PythonにArray.forEach()に相当する関数はない
items = ["a", "b", "c"]

for index, item in enumerate(items):
    print(f"{item}, {index}")
C#
1
2
3
4
5
string[] items = ["a", "b", "c"];
items.Select((item, index) => new { item, index }).ToList().ForEach(value =>
{
    Console.WriteLine($"{value.index}, {value.item}");
});

補足

Kotlin のメソッドは以下から確認した。

kotlin.collections - Kotlin Programming Language