関数型プログラミング: 第一級オブジェクト、スコープ、高階関数の包括的なガイド

関数型プログラミング

Python では、関数は第一級のオブジェクトです。これは、関数を他のオブジェクトと同様に操作および使用できることを意味します。関数型プログラミングでは、関数を引数として他の関数に渡したり、関数を変数に代入したり、関数を戻り値として返すこともできます。

第一級オブジェクトの特徴

ファーストクラスのオブジェクトには通常、次の特性があります。

  1. オブジェクトは実行時に作成されます。
  2. 変数に割り当てることも、データ構造内の要素として割り当てることもできます。
  3. パラメータとして渡すことができます。
  4. 戻り値として返すことができます。

範囲

スコープとは、変数が有効になる領域を指します。

b = 20 # 全局变量

def fn():
    a = 10 # a定义在了函数内部,所以他的作用域就是函数内部,函数外部无法访问
    print('函数内部:','a =',a)
    print('函数内部:','b =',b)

# fn()    
  

# print('函数外部:','a =',a)
# print('函数外部:','b =',b)

Python には 2 種類のスコープがあります。

グローバルスコープ

  • グローバル スコープはプログラムの実行時に作成され、プログラムの実行終了時に破棄されます。
  • 関数以外の領域はすべてグローバル スコープです。
  • グローバル スコープで定義された変数はグローバル変数であり、グローバル変数にはプログラム内のどこからでもアクセスできます。

関数スコープ

  • 関数スコープは関数の呼び出し時に作成され、呼び出しの終了時に破棄されます。
  • 関数が呼び出されるたびに、新しい関数スコープが作成されます。
  • 関数スコープで定義された変数はローカル変数であり、関数内でのみアクセスできます。

変数を検索します。

  • 変数を使用するときは、まず現在のスコープで変数を探します。変数があればそれを使用します。変数がなければ、引き続き上位スコープで変数を探します。が 1 つである場合、それを使用します。それでも見つからない場合は、上位レベルのスコープに進みます。グローバル スコープが見つかるまでスコープ内を検索し、それでも見つからない場合は、スコープ内を検索します。例外がスローされますNameError: name 'a' is not defined
def fn2():
    def fn3():
        print('fn3中:','a =',a)
    fn3()

# fn2()    

a = 20

def fn3():
    # a = 10 # 在函数中为变量赋值时,默认都是为局部变量赋值
    # 如果希望在函数内部修改全局变量,则需要使用global关键字,来声明变量
    global a # 声明在函数内部的使用a是全局变量,此时再去修改a时,就是在修改全局的a
    a = 10 # 修改全局变量
    print('函数内部:','a =',a)

# fn3()
# print('函数外部:','a =',a)

名前空間

ネームスペースとは変数が格納される場所を指し、各変数は指定されたネームスペースに格納される必要があります。各スコープには対応する名前空間があります。グローバル名前空間は、グローバル変数を保存するために使用されます。関数名前空間は、関数内に変数を保存するために使用されます。名前空間は実際には辞書であり、変数を格納するために特別に使用される辞書です。

# locals()用来获取当前作用域的命名空间
# 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
# 返回的是一个字典
scope = locals() # 当前命名空间
print(type(scope))
# print(a)
# print(scope['a'])
# 向scope中添加一个key-value
scope['c'] = 1000 # 向字典中添加key-value就相当于在全局中创建了一个变量(一般不建议这么做)
# print(c)

def fn4():
    a = 10
    # scope = locals() # 在函数内部调用locals()会获取到函数的命名空间
    # scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是也是不建议这么做

    # globals() 函数可以用来在任意位置获取全局命名空间
    global_scope = globals()
    # print(global_scope['a'])
    global_scope['a'] = 30
    # print(scope)

fn4()

練習する

  • 階乗を求める
  • 再帰関数
  • べき乗関数
  • テストコード

階乗を求める

# 创建一个函数,可以用来求任意数的阶乘
def factorial(n):
    '''
        该函数用来求任意数的阶乘

        参数:
            n 要求阶乘的数字
    '''

    # 创建一个变量,来保存结果
    result = n
    
    for i in range(1,n):
        result *= i

    return result    

再帰関数

# 创建一个函数,用来检查一个任意的字符串是否是回文字符串,如果是返回True,否则返回False
def hui_wen(s):
    '''
        该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False

        参数:
            s:就是要检查的字符串
    '''

    # 基线条件
    if len(s) < 2 :
        # 字符串的长度小于2,则字符串一定是回文
        return True
    elif s[0] != s[-1]:
        # 第一个字符和最后一个字符不相等,不是回文字符串
        return False    
    
    # 递归条件    
    return hui_wen(s[1:-1])

べき乗関数

# 创建一个函数 power 来为任意数字做幂运算 n ** i
def power(n , i):
    '''
        power()用来为任意的数字做幂运算

        参数:
            n 要做幂运算的数字
            i 做幂运算的次数
    '''

    # 基线条件
    if i == 1:
        # 求1次幂
        return n
    
    # 递归条件
    return n * power(n , i-1)

テストコード

# 求10的阶乘    
print(factorial(10))

# 检查字符串是否回文
print(hui_wen('abcdefgfedcba'))

# 对10进行5次幂运算
print(power(10, 5))

高次関数

高階関数とは、次のいずれかの条件を満たす関数です。

  1. 1 つ以上の関数をパラメータとして受け取ります。
  2. 関数を戻り値として返します。

高階関数は関数型プログラミングでは非常に一般的です。これらにより、関数をより柔軟に処理し、関数をデータとして操作したり渡したりできるようになります。

関数をパラメータとして受け取ったり、関数を戻り値として返す関数は高階関数です

関数をパラメータとして使用する場合、実際には指定されたコードをターゲット関数に渡します。

# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]

# 定义一个函数
#   可以将指定列表中的所有的偶数,保存到一个新的列表中返回

# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :
    if i % 2 == 0 :
        return True

    return False    

# 这个函数用来检查指定的数字是否大于5
def fn3(i):
    if i > 5 :
        return True    
    return False

def fn(func , lst) :

    '''
        fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回

        参数:
            lst:要进行筛选的列表
    '''
    # 创建一个新列表
    new_list = []

    # 对列表进行筛选
    for n in lst :
        # 判断n的奇偶
        if func(n) :
            new_list.append(n)
        
    # 返回新列表
    return new_list

関数を戻り値として返すことも高階関数です

Python では、高階関数と呼ばれる関数を戻り値として返すことができます。高階関数を使用すると、現在の関数のみがアクセスできる変数を作成できます。このような関数はクロージャと呼ばれます。

def fn():
    a = 10

    # 函数内部再定义一个函数
    def inner():
        print('我是fn2', a)

    # 将内部函数inner作为返回值返回   
    return inner

# r是一个函数,是调用fn()后返回的函数
# 这个函数实在fn()内部定义,并不是全局函数
# 所以这个函数总是能访问到fn()函数内的变量
r = fn()

上記のコードでは、fn()内部で別の関数を定義しinner()inner()その関数を戻り値として返す関数を定義しています。関数を呼び出すとfn()、関数オブジェクトを取得します。rこれを介して関数rを呼び出すことができinner()inner()関数はfn()関数内の変数にアクセスできますa

複数の数値の平均を求める

def make_averager():
    nums = []

    def averager(n):
        nums.append(n)
        return sum(nums)/len(nums)

    return averager

averager = make_averager()

print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))

make_averager()上記のコードでは、内部関数を返す関数を定義しましたaverager()この関数を呼び出すことでmake_averager()、平均を計算するクロージャを取得できますaverager関数が呼び出されるたびにaverager()、リストに値を追加しnums、現在のすべての値の平均を返します。

関数を複数回呼び出してaverager()、さまざまな数値シーケンスの平均を計算できますが、クロージャーの特性により、正しい平均を取得するために以前の値をすべて記憶します。

フィルター()

filter() は、条件を満たす要素をシーケンスから除外し、新しいシーケンスに保存できます

  1. シーケンスをフィルタリングする関数 (反復可能な構造)
  2. フィルタリングが必要なシーケンス (反復可能構造)
    戻り値:
    新しいフィルタリングされたシーケンス (反復可能構造)
def fn4(i):
    return i % 3 == 0
        
r = filter(lambda i : i > 5 , l)

地図()

map() 関数は、反復可能オブジェクト内のすべての要素に対して指定された操作を実行し、それらを新しいオブジェクトに追加してそれを返すことができます。

l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i : i ** 2 , l)

選別()

このメソッドは、リスト内の要素をソートするために使用されます。sort
() メソッドは、デフォルトで、リスト内の要素のサイズを直接比較します。sort
() は、キーワード パラメータを受け取ることができます。key キーには、
パラメータとして関数が必要です。関数はパラメータとして設定されます。
リスト内の要素をパラメータとして関数が呼び出されるたびに、関数の戻り値が要素のサイズを比較するために使用されます。

l = ['bb','aaaa','c','ddddddddd','fff']
# l.sort(key=len)

l = [2,5,'1',3,'6','4']
l.sort(key=int)

ソート済み()

この関数の使用法は基本的にsort()と同じですが、sorted()は任意のシーケンスをソートでき
、sorted()を使用したソートは元のオブジェクトには影響しませんが、新しいオブジェクトを返します。

l = [2,5,'1',3,'6','4']
# l = "123765816742634781"

print('排序前:',l)
print(sorted(l,key=int))
print('排序后:',l)

減らす()

このメソッドは、map() や filter() とは異なり、
reduce() は functools モジュールをインポートする必要があります。

from functools import reduce

l = [1,2,3,4,5]
r = reduce(lambda x,y : x + y , l)

練習する

filter 関数を使用してリスト内のすべての偶数を検索し、map 関数を使用して各偶数を文字列型に変換し、sorted 関数を使用して並べ替えます。

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

even_nums = filter(lambda n: n % 2 == 0, l)
str_nums = map(str, even_nums)
sorted_nums = sorted(str_nums)
print(sorted_nums)

出力:

['2', '4', '6', '8', '10']

デコレーター

デコレータは、他の関数の機能を変更または拡張するために使用できる特別なタイプの関数です。@デコレータは通常、ターゲット関数に適用されるPython 構文を使用します。デコレータは、元の関数コードを変更せずに元の関数をラップすることで、追加の動作を追加できます。

デコレータの例のコードは次のとおりです。

def decorator(func):
    def wrapper(*args, **kwargs):
        print("装饰器添加的额外功能")
        return func(*args, **kwargs)
    return wrapper

@decorator
def target_function():
    print("目标函数")

target_function()

出力:

装饰器添加的额外功能
目标函数

上記のコードでは、decoratorは関数を引数として受け取り、新しいラッパー関数 を返すデコレータ関数ですwrapperラッパー関数は、ターゲット関数を呼び出す前に追加の機能を追加します。デコレータを適用すると、呼び出されたときに追加の機能を取得target_functionできますtarget_function

いくつかの関数を作成する

def add(a , b):
    '''
        求任意两个数的和
    '''
    r = a + b
    return r


def mul(a , b):
    '''
        求任意两个数的积
    '''
    r = a * b
    return r    

計算前に計算の開始を出力し、計算後に計算の完了を出力できる機能が望まれます。この要件を満たすために関数内のコードを直接修正することもできますが、
①修正する関数が多すぎると修正が面倒になる、
②その後のメンテナンスに不便になる、
③という問題が発生します。そして、そうすることは、オープンクローズド原則 (OCP) に違反することになります。つまり、プログラムの設計には、プログラムの拡張機能の開発と、プログラムの変更のクローズが必要になります。

r = add(123,456)
print(r)

元の機能を変更せずに機能を拡張したいと考えています。

def fn():
    print('我是fn函数....')

# 只需要根据现有的函数,来创建一个新的函数
def fn2():
    print('函数开始执行~~~')
    fn()
    print('函数执行结束~~~')

fn2() 
def new_add(a,b):
    print('计算开始~~~')
    r = add(a,b)
    print('计算结束~~~')
    return r

r = new_add(111,222)    
print(r)

上記の方法であれば、ソースコードを変更せずに機能を拡張することができます。しかし、この方法では関数を拡張するたびに手動で新しい関数を作成する必要があり、非常に面倒です。この問題を解決するために、関数を自動的に生成するのに役立つ関数を作成します。

def begin_end(old):
    '''
        用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束

        参数:
            old 要扩展的函数对象
    '''
    # 创建一个新函数
    def new_function(*args , **kwargs):
        print('开始执行~~~~')
        # 调用被扩展的函数
        result = old(*args , **kwargs)
        print('执行结束~~~~')
        # 返回函数的执行结果
        return result

    # 返回新函数        
    return new_function

f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)

r = f()
r = f2(123,456)
r = f3(123,456)
print(r)

begin_end()この種の関数をデコレータと呼びます。デコレータを使用すると、元の関数を変更せずに関数を拡張できます。開発では、関数の機能を拡張するためにデコレータを使用します。

関数を定義するときに、@装饰器指定したデコレータを使用して現在の関数を修飾できます。関数に対して複数のデコレータを同時に指定すると、関数が内側から外側に向かって順番に装飾されるようになります。

def fn3(old):
    '''
        用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束

        参数:
            old 要扩展的函数对象
    '''
    # 创建一个新函数
    def new_function(*args , **kwargs):
        print('fn3装饰~开始执行~~~~')
        # 调用被扩展的函数
        result = old(*args , **kwargs)
        print('fn3装饰~执行结束~~~~')
        # 返回函数的执行结果
        return result

    # 返回新函数        
    return new_function

@fn3
@begin_end
def say_hello():
    print('大家好~~~')

say_hello()

要約する

関数型プログラミングは、計算プロセスを数学関数の構成として見るプログラミング パラダイムです。関数型プログラミングでは、関数は第一級のオブジェクトとみなされ、次の特徴があります。

  1. パラメーターとして渡す: より柔軟な機能を実現するために、関数をパラメーターとして他の関数に渡すことができます。
  2. 戻り値として返される: 関数を別の関数の戻り値として返して、カスタマイズ可能な動作を実現できます。
  3. 変数に割り当て可能: 関数を変数に割り当てて、さらに使用することができます。

スコープとは、変数が定義されるプログラム内の領域を指し、変数の可視性とライフサイクルを決定します。共通スコープには、グローバル スコープと関数スコープが含まれます。

グローバル スコープはプログラム全体からアクセスできるスコープですが、関数スコープは関数内でのみ表示されます。

ネームスペースは、変数名と関数名を格納するためのコンテナです。これは、名前を特定のスコープ内のオブジェクトに関連付ける方法を提供します。

実践的な演習では、関数型プログラミングの概念を使用して次の機能を実装しました。

  1. 階乗の検索: 階乗の検索機能は再帰関数によって実現されます。
  2. べき乗関数: 数値をべき乗する関数は、高次関数によって実現されます。
  3. 複数の数値の平均を求める: 複数の数値の平均を求める機能は、高階関数とreduce()関数によって実現されます。

デコレーターは、既存の関数の動作を変更する関数です。元の関数コードを変更することなく、関数に新しい関数を追加できます。実践的な演習では、デコレータを使用して関数を拡張します。

概要: 関数型プログラミングは、関数をファーストクラスのオブジェクトとして扱うことで、プログラムをより柔軟でスケーラブルにします。これには、ファーストクラスのオブジェクト、スコープ、名前空間などの概念が含まれており、高階関数、再帰関数、デコレータなどを通じてさまざまな関数を実装できます。関数型プログラミングは、複雑な問題の解決を簡素化する強力なプログラミング パラダイムです。

おすすめ

転載: blog.csdn.net/qq_41308872/article/details/132838449