<jupyter_start><jupyter_text>6-2. 高階関数Pythonにおける高階関数について説明します。参考- https://docs.python.org/ja/3/howto/functional.html  `max`例として、関数 **`max`** について考察します。`max` は与えられたリストの要素のうち、最大のものを返します。<jupyter_code>ls = [3,-8,1,0,7,-5]
max(ls)<jupyter_output><empty_output><jupyter_text>`max` に **`key`** というキーワード引数として、たとえば関数 `abs` を与えることができます。（キーワード引数について詳しくは、3-3を参照してください。）<jupyter_code>max(ls, key=abs)<jupyter_output><empty_output><jupyter_text>この場合、各要素に関数 `abs` が適用されて、その結果が最も大きい要素が返ります。（各要素に `abs` 適用した結果の中の最大値が返るわけではないことに注意してください。）なお、`abs(x)` は `x` の絶対値を返します。この場合、`max` という関数は、関数を引数として受け取っています。一般に、関数を引数として受け取ったり返値として返したりする関数を**高階関数**といいます。  `sorted`**`sorted`** も高階関数で、`max` と同様に **`key`** というキーワード引数を取ります。<jupyter_code>sorted(ls, key=abs)<jupyter_output><empty_output><jupyter_text>このように、各要素に関数 `abs` を適用した結果によって、各要素をソートします。リストを降順にソートするには、次のような関数を用いればよいです。<jupyter_code>def invert(x):
    return -x
sorted(ls, key=invert)<jupyter_output><empty_output><jupyter_text>なお、リストを降順にソートするには、**`reverse`** というキーワード引数に `True` を指定するという方法もあります。<jupyter_code>sorted(ls, reverse=True)<jupyter_output><empty_output><jupyter_text>ラムダ式上の `invert` のような簡単な関数の場合、いちいち `def` で定義するのは面倒と思いませんか。そのようなときは、**`lambda`** を使った**ラムダ式**（または**無名関数**）を用いることができます。上の例は、以下のように書くことができます。<jupyter_code>sorted(ls, key=lambda x: -x)<jupyter_output><empty_output><jupyter_text>`lambda x: -x` という式は、`x` をもらって `-x` を返す関数を表します。`return` は書かないことに注意してください。さて、ここまで関数と呼んでいたものは、Pythonでは、オブジェクトの一種に他なりません。実際に、`abs` や `lambda x: -x` という式を値を調べてみてください。<jupyter_code>abs
lambda x: -x<jupyter_output><empty_output><jupyter_text>したがって、Pythonでは、関数を他の種類のデータ（数やリストや文字列など）と同様に、関数の引数にしたり、リストの要素にしたり、することができます。  リストからイテラブルへ以上の例では、`max` や `sorted` はリストを受け取っていましたが、リストではなく、タプルでもいいですし、文字列でも構いません。<jupyter_code>max((3,-8,1,0,7,-5))
sorted((3,-8,1,0,7,-5))
max('hello world')
sorted('hello world')<jupyter_output><empty_output><jupyter_text>すなわち、`max` や `sorted` は、一般にイテラブルを引数に取ることができます。イテラブルについては4-2に説明がありましたが、簡単に言うと、**イテラブル**とはfor文の `in` の後に来ることができるものです。`max` や `sorted` は、イテラブルの各要素を次々と求めて、その中の最大値を求めたり、整列した結果をリストとして返したりします。以下の例では、`max` にファイルオブジェクトが渡されます。ファイルオブジェクトはイテレータですので、イテラブルでもあります。ファイルオブジェクトをfor文の `in` の後に指定すると、ファイルの各行が文字列として得られます。以下の例では、`key` として関数 `len` が指定されていますので、ファイルの中で最も長い行が表示されます。<jupyter_code>with open('jugemu.txt', 'r', encoding='utf-8') as f:
    print(max(f, key=len))<jupyter_output><empty_output><jupyter_text>辞書もイテラブルです。`max` に辞書与えると、最大のキーが返ります。<jupyter_code>max({3:10, 5:2, 9:1})<jupyter_output><empty_output><jupyter_text>練習辞書 `d` が与えられたとき、最大の値を持つキー（複数個ならばそのいずれか）を返す関数 `max_value_key(d)` を、`max` を使って定義してください。ヒント：辞書 `d` のキー `k` に対して、`k` に対応する値を返す関数は `lambda k: d[k]` という式で表すことができます。<jupyter_code>def max_value_key(d):
    ...<jupyter_output><empty_output><jupyter_text>上のセルで解答を作成した後、以下のセルを実行し、実行結果が `True` になることを確認してください。<jupyter_code>print(max_value_key({3:10, 5:2, 9:1}) == 3)<jupyter_output><empty_output><jupyter_text>`map`以下は内包表記の例です。<jupyter_code>[abs(x) for x in [3,-8,1,0,7,-5]]<jupyter_output><empty_output><jupyter_text>リストの各要素に関数 `abs` が適用された結果がリストになります。同様のことを、高階関数 **`map`** を用いて行うことができます。関数 `map` は、2番目の引数としてイテラブルを取ります。1番目の引数は関数です。例を見ましょう。<jupyter_code>map(abs, [3,-8,1,0,7,-5])<jupyter_output><empty_output><jupyter_text>何が返ったか、よくわからないと思います。以下のように、`map` の結果をfor文の `in` の後に指定してみましょう。<jupyter_code>for x in map(abs, [3,-8,1,0,7,-5]):
    print(x)<jupyter_output><empty_output><jupyter_text>すなわち、`map` が返すものは**イテレータ**です。このイテレータは、2番目の引数のイテラブル（この例ではリスト）の各要素に関数 `abs` を適用したものを、次々と返すようなイテレータです。内包表記を使えば、以下のようにリストにまとめることができます。<jupyter_code>[x for x in map(abs, [3,-8,1,0,7,-5])]<jupyter_output><empty_output><jupyter_text>関数 `list` をイテレータに適用してもよいです。<jupyter_code>list(map(abs, [3,-8,1,0,7,-5]))<jupyter_output><empty_output><jupyter_text>しかし、これでは、イテレータがどのように動くのか、よくわからないと思います。以下では、呼ばれるたびにメッセージを出力する関数 `abs1` を用います。<jupyter_code>def abs1(x):
    print('abs called on', x)
    return abs(x)
it = map(abs1, [3,-8,1,0,7,-5])<jupyter_output><empty_output><jupyter_text>このように、`map` がイテレータを返した時点では、各要素に対する計算は何も行われません。このイテレータに `next` を適用するとどうなるか、見てください。<jupyter_code>next(it)
next(it)<jupyter_output><empty_output><jupyter_text>関数 `next` が呼ばれるたびに、次の要素を求める計算が行われていることがわかります。イテレータはイテラブルですから、`map` の結果にさらに `map` を適用することができます。<jupyter_code>list(map(lambda x: x+1, map(abs, [3,-8,1,0,7,-5])))<jupyter_output><empty_output><jupyter_text>`lambda x: x+1` は、`x` をもらって `x+1` を返す関数です。すなわち、引数に `1` を足した結果を返します。関数 `sum` は、`max` と同様に、イテラブルを受け取って、その要素の総和を返します。したがって、`map` が返したイテレータに対しても適用できます。（イテレータをリストに変換する必要はありません。）<jupyter_code>sum(map(lambda x: x+1, map(abs, [3,-8,1,0,7,-5])))<jupyter_output><empty_output><jupyter_text>練習数のリストが与えられたとき、その要素の絶対値の最大値を返す関数 `max_abs` を、`map` と `max` を使って定義してください。<jupyter_code>def max_abs(ln):
    ...<jupyter_output><empty_output><jupyter_text>上のセルで解答を作成した後、以下のセルを実行し、実行結果が `True` になることを確認してください。<jupyter_code>print(max_abs([3,-8,1,0,7,-5]) == 8)<jupyter_output><empty_output><jupyter_text>`filter`関数 **`filter`** もイテラブルをもらってイテレータを返します。最初の引数としては、真理値を返す関数を指定します。<jupyter_code>def pos(x):
    if x>0:
        return True
    else:
        return False<jupyter_output><empty_output><jupyter_text>この関数 `pos` は、引数が正ならば `True`、そうでなければ `False` を返します。すると、以下のように、`filter` は `pos` を適用すると `True` が返る要素のみからなるイテレータを返します。<jupyter_code>list(filter(pos, [3,-8,1,0,7,-5]))<jupyter_output><empty_output><jupyter_text>`filter` は、条件付き内包表記に対応しています。同じ計算を以下のようにして行うことができます。<jupyter_code>[x for x in [3,-8,1,0,7,-5] if pos(x)]<jupyter_output><empty_output><jupyter_text>練習数のリスト `ln` と数 `n` を受け取って、`ln` の要素のうち、`n` より大きい個数を返す関数`number_of_big_numbers(ln, n)` を、for文やwhile文を用いずに、`filter` を用いて定義してください。<jupyter_code>def number_of_big_numbers(ln, n):
    ...<jupyter_output><empty_output><jupyter_text>上のセルで解答を作成した後、以下のセルを実行し、実行結果が `True` になることを確認してください。<jupyter_code>print(number_of_big_numbers([10, 0, 7, 1, 5, 2, 9], 5) == 3)<jupyter_output><empty_output><jupyter_text>練習ファイル名 `file` と整数 `n` を受け取って、そのファイルをオープンし、（改行文字も含めて）長さが `n` より長い行の数を返す関数 `number_of_long_lines(file,n)` を定義してください。（ファイルは `encoding='utf-8'` でオープンしてください。）<jupyter_code>def number_of_long_lines(file, n):
    ...<jupyter_output><empty_output><jupyter_text>上のセルで解答を作成した後、以下のセルを実行し、実行結果が `True` になることを確認してください。<jupyter_code>print(number_of_long_lines('jugemu.txt', 10) == 6)<jupyter_output><empty_output><jupyter_text>練習の解答<jupyter_code>def max_value_key(d):
    return max(d, key=lambda k: d[k])
def max_abs(ln):
    return max(map(abs, ln))
def number_of_big_numbers(ln, n):
    return sum(map(lambda x: 1, filter(lambda x: x>n, ln)))
def number_of_long_lines(file, n):
    with open(file, 'r', encoding='utf-8') as f:
        return sum(map(lambda x: 1, filter(lambda x: len(x)>n, f)))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16910544576859254, 'fraction_numerical': 0.020666767234877054, 'mean_word_length': 13.35064935064935, 'pattern_counts': {'":': 0, '<': 125, '<?xml version=': 0, '>': 128, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '31', 'dir': 'jupyter-structured-clean-dedup', 'id': '573065', 'license': 'MIT', 'n_tokens_mistral': 4534, 'n_tokens_neox': 4192, 'n_words': 304, 'path': '6/6-2.ipynb', 'repo_name': 'salikh/utpython', 'size': '6629'}