ラムダ関数と高階関数

# 1. 要約

**ラムダ関数**: 匿名関数と呼ばれる**名前のない関数**。一回だけの処理あるいは短く書きたい処理に向いている。（例: ソートやフィルター処理など）

**高階関数**: 関数を引数として受け取るか、または結果として関数を返す関数のこと。（例: filter, map, reduceなど）

- `map` 関数: 配列（リスト）の**各要素**に処理を適用して、新しいリストを返す  
- `filter` 関数: 配列（リスト）の要素のうち、**条件を満たすものだけを抽出**する  
- `reduce` 関数: 配列（リスト）の要素を**1つの値に畳み込む**（累積処理を行う）


# 2. ラムダ関数

## 2.1. ラムダ関数とは？

ラムダ関数とは「匿名関数」とも呼ばれ、通常の関数のように事前に定義する必要が無く、その場で関数リテラルとしてその場で作成される関数である。通常の関数とは異なり、その場で作成され、名前が固定されていない。そのため、柔軟性が高く、永続的な名前を付けずに使用することができる。例えば、配列（リスト）のソート時にソートの基準をラムダ関数を用いてかくことで、柔軟性を持ったソートを行うことができる。

## 2.2. コード（ラムダ関数）

```python
print("\n---- リストのソート ----\n")

""" 
リストのソート時にlambda式を使うことで、
リストを柔軟にソートすることができる。
"""

nums = [56, 91, 100, 68, 44, 21, 20, 36, 95, 15]

sorted_by_ascending = sorted(nums) # sorted はデフォルトで昇順にソートする
sorted_by_descending = sorted(nums, key=lambda x: -x) # 降順にソートするには key に負の値を指定する
sorted_by_original = sorted(nums, key=lambda x: nums.index(x)) # 元の順番にソートするには index を指定する(やる必要はないんだけど。)
sorted_by_ones = sorted(nums, key=lambda x: x % 10) # 1の位でソートするには % 10 を指定する

print("昇順:", sorted_by_ascending)
print("降順:", sorted_by_descending)
print("元の順番:", sorted_by_original)
print("1の位でソート:", sorted_by_ones)
print("元のリスト:", nums) # 元のリストは変更されない

print("\n---- タプルのソート ----\n")

""" 
タプルもリストと同様にlambda式を使うことで、柔軟にソートすることができる。
(A, B)のうち、最初の要素で昇順に、次の要素で降順にソートするといった複雑なソートも可能。
"""

tuples = [(1, 2), (22, 31), (22, 11), (3, 5), (3, 1), (2, 41), (2, 13), (1, 1)]

sorted_by_first = sorted(tuples, key=lambda x: x[0])  # 第1要素（A）で昇順
sorted_by_first_then_second_desc = sorted(tuples, key=lambda x: (x[0], -x[1]))  # A昇順 → B降順

print("第1要素で昇順:", sorted_by_first)
print("第1要素昇順・第2要素降順:", sorted_by_first_then_second_desc)
```

## 2.3. 実行結果（ラムダ関数）

```
---- リストのソート ----

昇順: [15, 20, 21, 36, 44, 56, 68, 91, 95, 100]
降順: [100, 95, 91, 68, 56, 44, 36, 21, 20, 15]
元の順番: [56, 91, 100, 68, 44, 21, 20, 36, 95, 15]
1の位でソート: [100, 20, 91, 21, 44, 95, 15, 56, 36, 68]
元のリスト: [56, 91, 100, 68, 44, 21, 20, 36, 95, 15]

---- タプルのソート ----

第1要素で昇順: [(1, 2), (1, 1), (2, 41), (2, 13), (3, 5), (3, 1), (22, 31), (22, 11)]        
第1要素昇順・第2要素降順: [(1, 2), (1, 1), (2, 41), (2, 13), (3, 5), (3, 1), (22, 31), (22, 11)]
```

# 3. 高階関数

## 3.1. 高階関数とは？

高階関数とは、関数を引数として受け取るか、または結果として関数を返す関数のことである。

### 3.1.1. コード（高階関数: 入力として関数を受け取る）

```python
""" 
高階関数は、関数を引数として受け取ったり、結果として関数を返す関数のことを指します。
"""

def higher_order_function(func):
    """
    高階関数の例 -> 引数として関数を受け取る
    """
    
    print("---- 処理の始まり ----")
    func() # 引数として渡された関数を実行
    print("---- 処理の終わり ----")
    
def say_hello():
    """
    引数として渡す関数の例
    """
    print("Hello, World!")
    
higher_order_function(say_hello) # say_hello関数を引数として渡す
```

### 3.1.2. 実行結果（高階関数: 入力として関数を受け取る）

```
---- 処理の始まり ----
Hello, World!
---- 処理の終わり ----
```

3.1.1. のコードでは、higher_order_functionは関数を引数として受け取り、処理を実行している。

### 3.1.3. コード（高階関数: 出力として関数を返す）

```python
""" 
高階関数は、関数を引数として受け取ったり、結果として関数を返す関数のことを指します。
"""

def higher_order_function():
    """
    高階関数の例 -> 結果として関数を返す
    """
    
    def inner_function():
        print("---- 高階関数の実行 ----")
        
    return inner_function()

print("---- 処理の始まり ----")
higher_order_function() # 結果として関数を返す
print("---- 処理の終わり ----")
```

### 3.1.4. 実行結果（高階関数: 出力として関数を返す）

```python
---- 処理の始まり ----
---- 高階関数の実行 ----
---- 処理の終わり ----
```

3.1.3. のコードでは、higher_order_functionは関数である`inner_function`を結果として返している。

高階関数を使用する事で、処理の責務を分離し、複数の関数を組合わせることができる。これにいより、コードの抽象化や再利用性の向上が図れる。多くの言語では、`map`、`filter`、`reduce`などの関数があらかじめ用意されており、高階関数として利用することができる。

## 3.2. map関数（高階関数）

map関数とは、リストや配列の各要素に与えられた関数を適用し、変更後の値を新しいリストや配列として返すことができる。

### 3.2.1. コード（高階関数: map関数）

```python
""" 
map関数: 各要素に対して関数を適用する。
"""

def triple(x):
    return x * 3

nums = [1, 2, 3, 4, 5]

# map関数を使って、リストの各要素にtriple関数を適用する
# map(関数, イテラブル)の形で使用する。
# map関数はイテラブルを返すので、list()でリストに変換する必要がある。
result = map(triple, nums)
print(f"3倍適用前: {nums}")  # [1, 2, 3, 4, 5]
print(f"3倍適用後: {list(result)}")  # [3, 6, 9, 12, 15]
print(f"3倍適用後 - イテラブル: {result}")
```

### 3.2.2. 実行結果（高階関数: map関数）

```python
3倍適用前: [1, 2, 3, 4, 5]
3倍適用後: [3, 6, 9, 12, 15]
3倍適用後 - イテラブル: <map object at 0x0000020895F07AF0>
```

## 3.3. filter関数（高階関数）

filter関数は、述語関数（= boolean値/真偽値 を返す関数）とリストを受けとり、リストの各要素に対して述語関数を実行して、フィルタリングを行う。

例として、リストに格納されている数字の内奇数のみを取り出すことを考えてみる。

### 3.3.1. コード（高階関数: filter関数）

```python
""" 
filter関数: イテラブルの各要素に関数を適用し、Trueを返す要素だけを抽出する。
"""

def is_odd(n):
    """奇数かどうかを判定する関数"""
    return n % 2 == 1

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# filter関数を使って、numsの中から奇数だけを抽出する
odd_nums = filter(is_odd, nums)

# odd_numsはイテレータなので、list関数でリストに変換して出力する必要がある。
# 直接print(odd_nums)をすると、filterオブジェクトが表示されるだけになる。
print(f"奇数のみのリスト: {list(odd_nums)}")  # [1, 3, 5, 7, 9]
print(f"奇数のみのリスト - イテレータ: {odd_nums}")
```

### 3.3.2. 実行結果（高階関数: filter関数）

```
奇数のみのリスト: [1, 3, 5, 7, 9]
奇数のみのリスト - イテレータ: <filter object at 0x00000274E4C87A30>
```

## 3.4. reduce関数（高階関数）

reduce関数は、リストなどのシーケンスを1つの値に”畳み込む”ための関数。例えばリストの合計や、積を求めたいときに使用する。

reduce関数は、3つの引数を取る：

1. 2つの値を受け取って結果を返す関数
2. 処理対象のシーケンス（リストなど）
3. （任意の初期値）

### 3.4.1. コード（高階関数: reduce関数）

```python
""" 
reduce関数: イテラブルの各要素に関数を適用し、累積的に1つの値にまとめる。
"""

from functools import reduce

nums = [1, 2, 3, 4]

result = reduce(lambda x, y : x + y, nums, 0)
# 処理の流れ：
# step1: 0 + 1 = 1
# step2: 1 + 2 = 3
# step3: 3 + 3 = 6
# step4: 6 + 4 = 10

print(result) # 10

result_started_by_100 = reduce(lambda x, y : x + y, nums, 100)
# 処理の流れ：
# step1: 100 + 1 = 101
# step2: 101 + 2 = 103
# step3: 103 + 3 = 106
# step4: 106 + 4 = 110

print(result_started_by_100) # 110
```

### 3.4.2. 実行結果（高階関数: reduce関数）

```python
リストnums : [1, 2, 3, 4]
numsの合計(初期値: 0): 10
numsの合計(初期値: 100): 110
```

# 4. 終わりに
もし間違いがあれば、コメントで教えてください。