Python 課題集: リスト操作

# はじめに

Python のコーディング課題です。
リスト操作を行うものを集めました。

# リストの長さを求める

**課題: 実装してください。**

リストの長さを求める関数 `length(values)` を実装してください。
色々な実装方法があるので、思いつく限りのパターンで実装してみましょう。
関数のシグネチャは以下とします。

```python
def length(values):
    """
        リストの長さを求める.

        Examples
        --------
        >>> length([])
        0
        >>> length([1, 2, 3])
        3
        >>> length(1)
        Traceback (most recent call last):
        ...
        ValueError

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        length : int
            values の長さ.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**実装例**

次のように色々な実装方法があります。

**実装例: len() を使う方法**

```python
# len() を使う方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    return len(values)
```

**実装例: reduce() を使う方法**

```python
import fulctools

# functools.reduce() を使う方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    return functools.reduce(lambda count, value: count + 1, values, 0)
```

**実装例: map() で全要素を 1 に変換してから合計する方法**

```python
# map() で全要素を 1 に変換してから合計する方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    return sum(map(lambda value: 1, values))
```

**実装例: リスト内包表記で全要素を 1 に変換してから合計する方法**

```python
# リスト内包表記で全要素を 1 に変換してから合計する方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    return sum([1 for value in values])
```

**実装例: ループしながらカウントする方法**

```python
# ループを使う方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    count = 0
    for value in values:
        count += 1
    return count
```

**実装例: 再帰を使う方法**

```python
# 再帰を使う方法
def length(values):
    if not isinstance(values, list):
        raise ValueError

    if values == []:
        return 0
    return 1 + length(values[1:])
```

**実装例: 末尾再帰を使う方法**

```python
# 末尾再帰を使う方法
def length(values):
    def _length(values, count=0):
        if values == []:
            return count
        return _length(values[1:], count + 1)

    if not isinstance(values, list):
        raise ValueError

    return _length(values)
```

**課題: 写経してください。**

思いつかなかったパターンがあれば写経してください。

**課題: 考えてください。**

それぞれの実装方法の特徴を考えてください。
実装上の特徴や、良い点、悪い点があるはずです。

ヒント: 実行速度、メモリ使用量、実装難易度、読みやすさ、etc..

**課題: テストしてください。**

`length(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

**課題: 不具合を混入してください。**

テストをなるべく壊さないように、`length(values)` に不具合を混入してください。

**課題: テストケースを改良してください。**

テストを全く壊さずに不具合を混入できた場合は、その不具合を検出できるテストケースを追加してください。

**課題: 関数の仕様を改良してください。**

関数の仕様として不明確な点を探し、docstring を改良してください。

**テストの例**

まずは、大まかにどのようなパターンのテストケースがあるか考えます。

 * 引数が空のリストである場合
 * 引数が空ではないリストである場合
 * 引数がネストしたリストである場合
 * 引数がリストではない場合
 * 引数がリスト以外の iterable である場合

次に、それぞれのパターンについて具体的な値を考えます。

 * 引数が空のリストである場合
   * `[]` の場合
 * 引数が空ではないリストである場合
   * `[1]` の場合
   * `[1, 2]` の場合
   * `[1, 2, 3]` の場合
 * 引数がネストしたリストである場合
   * `[[]]` の場合
   * `[1, 2, [3, 3]]` の場合
   * `[1, [2, 2], 3]` の場合
   * `[[1, 1], 2, 3]` の場合
   * `[1, [2], [[3]]]` の場合
   * `[[1, 1, [1]], [2, [2], 2], [[3], 3, 3]]` の場合
 * 引数がリストではない場合
   * `1` の場合
   * `None` の場合
 * 引数がリスト以外の iterable である場合
   * `{1, 2, 3}` の場合 (set の場合)
   * `(1, 2, 3)` の場合 (tuple の場合)
   * `{1: "one", 2: "two", 3: "three"}` の場合 (dict の場合)
   * `"123"` の場合 (str の場合)

テストケースを洗い出したら、それをテストコードとして実装します。

```python
import unittest


def length(values):
    pass # TODO ここを実装する


class LengthTestCase(unittest.TestCase):

    def test_when_empty_list_passed(self):
        self.assertEqual(0, length([]))

    def test_when_non_empty_list_passed(self):
        self.assertEqual(1, length([1]))
        self.assertEqual(2, length([1, 2]))
        self.assertEqual(3, length([1, 2, 3]))

    def test_when_nested_list_passed(self):
        self.assertEqual(1, length([[]]))
        self.assertEqual(3, length([1, 2, [3, 3]]))
        self.assertEqual(3, length([1, [2, 2], 3]))
        self.assertEqual(3, length([[1, 1], 2, 3]))
        self.assertEqual(3, length([[1, 1, [1]], [2, [2], 2], [[3], 3, 3]]))

    def test_when_non_list_passed(self):
        with self.assertRaises(ValueError):
            length(1)
        with self.assertRaises(ValueError):
            length(None)

    def test_when_non_list_iterable_passed(self):
        with self.assertRaises(ValueError):
            length({1, 2, 3})
        with self.assertRaises(ValueError):
            length((1, 2, 3))
        with self.assertRaises(ValueError):
            length({1: "one", 2: "two", 3: "three"})
        with self.assertRaises(ValueError):
            length("12345")


if __name__ == "__main__":
    unittest.main()
```

**課題: まとめてください。**

ここまでに学んだことをまとめてください。

# リストを反転する

**課題: 実装してください。**

リストの要素を反転する関数 `reverse(values)` を実装してください。
色々な実装方法があるので、思いつく限りのパターンで実装してみましょう。
関数のシグネチャは以下とします。

```python
def reverse(values):
    """ 
        リストを反転する.

        Examples
        --------
        >>> reverse([1, 2, 3])
        [3, 2, 1]

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        reversed_values : list[object]
            values を反転して生成したリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`reverse(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

**課題: 不具合を混入してください。**

テストをなるべく壊さないような不具合を `reverse(values)` に混入してください。

**課題: テストケースを改良してください。**

テストを全く壊さずに不具合を混入できた場合は、その不具合を検出できるテストケースを追加してください。

**課題: 関数の仕様を改良してください。**

関数の仕様として不十分な点を探し、docstring を改良してください。

# リストの要素数を求める

**課題: 実装してください。**

リストの要素数を求める関数 `shallow_count(values)` を実装してください。
関数のシグネチャは以下とします。

```python
def shallow_count(values):
    """ 
        リストの要素数を求める.

        Examples
        --------
        >>> shallow_count([])
        0
        >>> shallow_count([1, 2, 3])
        3
        >>> shallow_count(1)
        Traceback (most recent call last):
        ...
        ValueError

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        count : int
            values の要素数.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`shallow_count(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

**課題: 実装してください。**

ネストしたリストの要素数を求める関数 `deep_count(values)` を実装してください。
関数のシグネチャは以下とします。

```python
def deep_count(values):
    """
        ネストしたリストの要素数を求める.

        Examples
        --------
        >>> deep_count([1, 2, 3])
        3
        >>> deep_count([1, [2, 3], 4, 5])
        5

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        count : int
            values に含まれる要素数.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`deep_count(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストを変換する

**課題: 実装してください。**

リストの要素を変換する関数 `shallow_map(mapper, values)` を実装してください。
関数のシグネチャは以下とします。

```python
def shallow_map(mapper, values):
    """
        リストの要素を変換する.

        Examples
        --------
        >>> shallow_map(lambda x: x * 2, [1, 2, 3])
        [2, 4, 6]
        >>> shallow_map(str, [1, 2, 3])
        ['1', '2', '3']

        Arguments
        ---------
        mapper : callable
            リストの要素を変換するために用いる関数.
        values : list[object]
            任意のリスト.

        Returns
        -------
            values の各要素を mapper で変換して生成したリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`shallow_map(mapper, values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

**課題: 実装してください。**

ネストしたリストの要素を変換する関数 `deep_map(mapper, values)` を実装してください。
関数のシグネチャは以下とします。

```python
def deep_map(mapper, values):
    """ 
        ネストしたリストの要素を変換する.

        Examples
        --------
        >>> deep_map(lambda x: x * 2, [1, 2, 3])
        [2, 4, 6]
        >>> deep_map(lambda x: x * 2, [1, [2, 3]])
        [2, [4, 6]]

        Arguments
        ---------
        mapper : callable
            リストの要素を変換するために用いる関数.
        values : list[object]
            任意のリスト.

        Returns
        -------
            values の各要素を mapper で変換して生成したリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`deep_map(mapper, values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストを平坦化する

**課題: 実装してください。**

ネストした要素を平坦化し、一次元のリストに変換する関数 `flatten(values)` を実装してください。
関数のシグネチャは以下とします。

```python
def flatten(values):
    """ 
        ネストしたリストを平坦化する.

        Examples
        --------
        >>> flatten([1, [2, 3], 4])
        [1, 2, 3, 4]

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        flatten_values : list[object]
            values に含まれる値を平坦化して作成した一次元のリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`flatten(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストを分割する

**課題: 実装してください。**

リストを n 要素ずつのサブリストに分割する関数 `chunked(values, n)` を実装してください。
関数のシグネチャは以下とします。

```python
def chunked(values, n):
    """
        リストを n 要素で分割する.

        Examples
        --------
        >>> chunked([1, 2, 3, 4, 5], 2)
        [[1, 2], [3, 4], [5]]

        Arguments
        ---------
        values : list[object]
            任意のリスト.
        n : int
            分割後のサブリストに含まれる最大要素数.

        Returns
        -------
        chunks : list[list[object]]
            values を n 要素ずつに分割したリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`chunked(values, n)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストの重複要素を削除する

**課題: 実装してください。**

リスト中の重複要素を削除する関数 `unique(values)` を実装してください。
関数のシグネチャは以下とします。

```python
def unique(values):
    """ 
        リスト中の重複した値を削除する.

        Examples
        --------
        >>> unique([1, 2, 2, 3])
        [1, 2, 3]

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        values : list[object]
            values の重複要素を削除して生成したリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`unique(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストのヒストグラムを求める

**課題: 実装してください。**

リストに含まれる値の出現回数を求める関数 `histogram(values)` を実装してください。
関数のシグネチャは以下とします。

```python
def histogram(values):
    """ 
        ヒストグラムを求める.

        Examples
        --------
        >>> histogram([9, 5, 3, 9, 4, 5, 8, 8, 8, 2])
        {2: 1, 3: 1, 4: 1, 5: 2, 8: 3, 9: 2}

        Arguments
        ---------
        values : list[object]
            任意のリスト.

        Returns
        -------
        dict[object, int]
            値と出現回数を保持する dict.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`histogram(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストの並び替え

**課題: 実装してください。**

リストを並び替える関数 `sort(values)` を実装してください。
関数のシグネチャは以下とします。

```python

def sort(values):
    """ 
        リストを昇順に並び替える.

        Examples
        --------
        >>> sort([7, 3, 4, 6, 2, 8, 0, 5, 9, 1])
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

        Arguments
        ---------
        values : list[object]
            比較可能な値からなるリスト.

        Returns
        -------
        sorted_values : list[object]
            values を昇順に並び替えたリスト.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`sort(values)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# リストからの検索

**課題: 実装してください。**

リストから値を検索する関数 `search(values, target)` を実装してください。
関数のシグネチャは以下とします。

```python
def search(values, target):
    """
        リストから値を検索する.

        Examples
        --------
        >>> search([1, 3, 5, 7, 9], 5)
        2
        >>> search([1, 1, 1, 1, 1], 1)
        0
        >>> search([1, 1, 1, 1, 1], 7)
        -1

        Arguments
        ---------
        values : list[object]
            任意のリスト.
        target : object
            検索する値.

        Returns
        -------
        index : int
            values に含まれる最初の target のインデックス.
            values に target が含まれない場合は -1.

        Raises
        ------
        ValueError
            values がリストではない場合.
    """
    pass # TODO ここを実装する.
```

**課題: テストしてください。**

`search(values, target)` のテストケースを考えてください。
次にテストケースをテストコードとして実装し、実行してください。

# おわりに

おつかれさまでした。