[Python] 「関数型プログラミング」の概要

高洛峰
リリース: 2017-02-16 11:09:49
オリジナル
1306 人が閲覧しました

関数型プログラミング

先学期、私は「人工知能」という授業を受講しました。 うわー、それまで習っていたものと考え方が全く違っていて、とても違和感がありました。私はそれをハノイの塔のように考え、長い間、インターネット上でコードを見つけて、(先生に盗作が見つかることを恐れて)修正してから、投稿しました。感覚をつかんでください:

hanoi(N) :- dohanoi(N, 'a', 'b', 'c').
dohanoi(0, _ , _ , _ )    :- !.
dohanoi(N, A, B, C)    :-
  N1 is N-1,
  dohanoi(N1, A, C, B),
  writeln([move, N, A-->C]), 
  dohanoi(N1, B, A, C).
ログイン後にコピー

当時はほとんど理解できましたが、主に情報が少なすぎて、デバッグが不可能でした。バグに遭遇すると、今でも少しめまいを感じます。自分で見てる。しかし、当時は prolog が Lisp に対抗できると言われていたので、最近は Lisp に少し興味を持ち始めました。これらを終えた後、この種の関数型言語に敬意を表したいと思います。

関数型プログラミングとは何ですか? Liao Da はここにこう書きました:

関数型プログラミングは、高度な抽象度を備えたプログラミング パラダイムです。純粋な関数型プログラミング言語で記述された関数には変数がありません。したがって、入力が確実である限り、どの関数でも出力が決まります。確実であるため、この種の副作用のない純粋な関数を呼び出します。変数を使用できるプログラミング言語では、関数内の変数の状態が不定であるため、同じ入力でも異なる出力が得られる可能性があるため、この種の関数には副作用があります。

読んでもまだ理解できないかもしれませんが、心配しないで、最初にこれらのセクションを読んでください。

高次関数

数学とコンピューターサイエンスでは、高次関数とは、次の条件の少なくとも 1 つを満たす関数です:

  • 1 つ以上の関数を入力として受け入れる

  • 関数を出力する

また、関数自体をパラメーターとして渡すか、関数を返します。

たとえば、通常の割り当てと同じように関数を変数に割り当てることができます:

>>> min(1, 2)
1
>>> f = min
>>> f(1, 2)
1
>>> f
<built-in function min>
>>> min
<built-in function min>
ログイン後にコピー

関数に値を割り当てることもできます (コードは続きます):

>>> min = 10
>>> min(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> f(1, 2)
1
>>> min = f
>>> min(1, 2)
1
ログイン後にコピー

また、パラメータを渡すこともできます。たとえば、すべての数値の合計を計算します:

>>> def add(a, b):
...     return a+b
...

>>> def mysum(f, *l):
...     a = 0
...     for i in l:
...             a = f(a, i)
...     return a
...
>>> mysum(add, 1, 2, 3)
6
>>> mysum(add, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
55
ログイン後にコピー

もちろん、この f を乗算に置き換えることは、すべての数値の積を計算することを意味します。

頻繁に使用される、Python に組み込まれている高階関数のいくつかを見てみましょう。

map/reduce

この言葉は先学期にクラウドコンピューティングの授業を受講していた時になんとなく聞いた記憶がありましたが、授業がとても退屈だったのであまり聞いていませんでしたが、ここで見たときはそう思いました。何か違うものを見つけましたか? ?

しかし、言うことはあまりありません。各機能の役割について簡単に説明しましょう。

mapの場合、計算式は次のようになります:

map(f, [x1, x2, ..., xn]) = [f(x1), f(x2), ..., f(xn)]
ログイン後にコピー

reduceの場合、計算式は次のようになります:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
ログイン後にコピー

廖達ははっきりと言いました。

filter

filterは関数を受け取り反復可能でリストを返すというmap関数と似ていますが、その機能は関数の戻り値がTrueかどうかで値を保持するかどうかを判断することです。例:

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
ログイン後にコピー

sorted

sorted 関数も高階関数です。この関数をパラメーター key に渡すと、key 関数を通じて並べ替えられるシーケンスを並べ替えることができますが、シーケンスの値は変更されません。例:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
ログイン後にコピー

Decorator (デコレータ)

匿名関数については、後で使用するときに詳しく説明します。フラスコを見ていて、デコレータについて長い間勉強したことを思い出します。この時。

シンプルなデコレーター

1 つ目はシンプルなデコレーターで、各関数呼び出しの前にログを出力します:

import logging

def log(func):
    def wrapper(*args, **kw):
        logging.warn("%s is running" % func.__name__)
        func(*args, **kw)
    return wrapper
ログイン後にコピー

これは非常にシンプルなデコレーターですが、どうやって使用しますか?私が最初に見た使い方は、装飾が必要な関数の前に @ を追加するものでしたが、実際には、これは Python の糖衣構文であり、最初に関数 f:

def f():
    print("in function f")

f = log(f)
ログイン後にコピー

を定義する方がわかりやすいです。この後、再度 f 関数を呼び出します:

>>> f()
WARNING:root:f is running
in function f
ログイン後にコピー

@log を使用した結果は同じです。実際、@ 記号はデコレーターの構文糖として機能し、前の代入ステートメントと同じ機能を持ちます。コードがより簡潔かつ明確になり、次のような別の代入操作が不要になります:

@log
def f():
    print("in function f")
ログイン後にコピー

パラメータ付きデコレータ

場合によっては、ステータス、レベル、その他の情報などのパラメータをデコレータに渡す必要があることもあります。ラッパー関数の外側でそれを「ラップ」するだけです。レイヤー関数は次のとおりです:

import logging

def log(level):
    def decorator(func):
        def wrapper(*args, **kw):
            logging.warn("%s is running at level %d" % (func.__name__, level))
            return func(*args, **kw)
        return wrapper
    return decorator

@log(2)
def f():
    print("in function f")
    
>>> f()
WARNING:root:f is running at level 2
in function f
ログイン後にコピー

さらなる理解

デコレーターをさらに理解するために、関数 f:

#对于不加装饰器的 f,其 name 不变
>>> def f():
...     print("in function f")
...
>>> f.__name__
'f'

#对于添加装饰器的函数,其 name 改变了
>>> @log
... def f():
...     print("in function f")
...
>>> f.__name__
'wrapper'
ログイン後にコピー

Contact の name 属性を出力できます。最初のデコレータ代入ステートメントでは、何が起こったのか大まかに理解できます。 内容: f = log(f) f が log(f) として変更された戻り値、つまりラッパー関数を指すようにします。元の関数 f が実行されるたびに、ラッパー関数が呼び出されます。この例では、最初にログが出力されてから、元の関数 f が実行されます。

ただし、これには問題があります。これにより、元の関数 f のメタ情報が置き換えられ、f に関する多くの情報が失われます。しかし、幸いなことに、変更された functools モジュールがあります。関数は次のとおりです:

import functools
import logging

def log(func):
    functools.wraps(func)
    def wrapper(*args, **kw):
        logging.warn("%s is running" % func.__name__)
        func(*args, **kw)
    return wrapper

>>> @log
... def f():
...     print("in function f")
...
>>> f.__name__
'f'
ログイン後にコピー

さらに、同じ関数に複数のデコレータを追加することもできます:

@a
@b
@c
def f ():


# 等价于

f = a(b(c(f)))
ログイン後にコピー

概要

私は関数型プログラミングについてはあまり知りませんが、その概念については一般的に理解しています。日常生活における必須のプログラミング。しかし、HaskellやLispのように純粋に関数型の言語もあり、それらを学ぶことで新たな考え方が開けます。

「関数型プログラミング」に関連するその他の [Python] 記事については、PHP 中国語 Web サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート