Python函數語言程式設計術語大全

alphardex發表於2019-04-27

repo: github.com/alphardex/p…

參考repo:github.com/hemanth/fun…

Arity - 函式引數個數

import inspect
add = lambda a, b: a + b
len(inspect.getfullargspec(add).args)
# 2
複製程式碼

Higher-Order Function - 高階函式

以函式為引數或返回值

is_type = lambda type_: lambda x: isinstance(x, type_)
li = [0, '1', 2, None]
[l for l in li if is_type(int)(l)]
# [0, 2]
複製程式碼

Closure - 閉包

閉包是一種在變數作用域之外訪問變數的方法。是一種將函式儲存在環境中的方法。

閉包是一個作用域,它捕獲函式的區域性變數以便訪問,即使在執行已經移出定義它的塊之後也是如此。

add_to = lambda x: lambda y: x + y
add_to_five = add_to(5)
add_to_five(3)
# 8
複製程式碼

函式addTo()返回一個函式(內部稱為add()),將它儲存在名為addToFive的變數中,它帶有引數為5的柯里化呼叫。

理想情況下,當函式addTo完成執行時,其作用域與區域性變數add,x,y就變得不可訪問。 但是,它在呼叫addToFive()時返回8。 這意味著即使在程式碼塊完成執行後也會儲存函式addTo的狀態,否則無法知道addTo被呼叫為addTo(5)並且x的值被設定為5。

詞法作用域範圍是它能夠找到x和add的值的原因 - 已經完成執行的父項的私有變數。該值稱為閉包。

堆疊以及函式的詞法範圍以對父項的引用形式儲存。這可以防止關閉和底層變數被垃圾收集(因為至少有一個對它的實時引用)。

閉包是一種通過引用其主體外部的欄位來包圍其周圍狀態的函式。封閉狀態保持在閉包的呼叫之間。

Partial Function - 偏函式

通過對原始函式預設引數來建立一個新的函式

from functools import partial
add3 = lambda a, b, c: a + b + c
five_plus = partial(add3, 2, 3)
five_plus(4)
# 9
複製程式碼

Currying - 柯里化

將一個多元函式轉變為一元函式的過程

add = lambda a, b: a + b
curried_add = lambda a: lambda b: a + b
curried_add(3)(4)
# 7
add2 = curried_add(2)
add2(10)
# 12
複製程式碼

Auto Currying - 自動柯里化

from toolz import curry
add = lambda a, b: a + b
curried_add = curry(add)
curried_add(1, 2)
# 3
curried_add(1)(2)
# 3
curried_add(1)
# <function <lambda> at 0x000002088BBD5E18>
複製程式碼

Function Composition - 函式組合

接收多個函式作為引數,從右到左,一個函式的輸入為另一個函式的輸出

import math
from functools import reduce
# 組合2個函式
compose = lambda f, g: lambda a: f(g(a))
# 組合多個函式
compose = lambda *funcs: reduce(lambda f, g: lambda *args: f(g(*args)), funcs)
floor_and_to_string = compose(str, math.floor)
floor_and_to_string(12.12)
# '12'
複製程式碼

Purity - 純函式

輸出僅由輸入決定,且不產生副作用

greet = lambda name: f'hello, {name}'
greet('world')
'hello, world'
複製程式碼

以下程式碼不是純函式

# 情況1:函式依賴全域性變數
NAME = 'alphardex'
greet = lambda: f'hi, {NAME}'
greet()
# 'hi, alphardex'

# 情況2:函式修改了全域性變數
greeting = None
def greet(name):
    global greeting
    greeting = f'hi, {name}'
greet('alphardex')
greeting
# 'hi, alphardex'
複製程式碼

Side effects - 副作用

如果函式與外部可變狀態進行互動,則它是有副作用的

最典型的例子是建立日期和IO

from datetime import datetime
different_every_time = datetime.now()
different_every_time
# datetime.datetime(2019, 4, 20, 17, 30, 24, 824876)
different_every_time = datetime.now()
different_every_time
# datetime.datetime(2019, 4, 20, 17, 31, 41, 204302)
複製程式碼

Idempotent - 冪等性

如果一個函式執行多次皆返回相同的結果,則它是冪等性的

abs(abs(abs(10)))
# 10
複製程式碼

Point-Free Style - Point-Free 風格

定義函式時,不顯式地指出函式所帶引數,這種風格通常需要柯里化或者高階函式

Point-Free風格的函式就像平常的賦值,不使用def或者lambda關鍵詞

map_ = lambda func: lambda li: [func(l) for l in li]
add = lambda a: lambda b: a + b
increment_all = map_(add(1))

numbers = [1, 2, 3]
increment_all(numbers)
# [2, 3, 4]
複製程式碼

Predicate - 謂詞

根據輸入返回 True 或 False。常用於filter函式中

filter函式亦可以用列表推導式的if判斷實現

above_two =  lambda a: a > 2
li = [1, 2, 3, 4]
[l for l in li if above_two(l)]
# [3, 4]
複製程式碼

Contracts - 契約

契約保證了函式或者表示式在執行時的行為。當違反契約時,將丟擲一個錯誤。

def contract(input):
    if isinstance(input, int):
        return True
    raise Exception('Contract Violated: expected int -> int')

add_one = lambda num: contract(num) and num + 1
add_one(2)
# 3
add_one('hello')
# Exception Traceback
複製程式碼

Functor - 函子

一個實現了map函式的物件,map會遍歷物件中的每個值並生成一個新的物件。

Python中最具代表性的函子就是list, 因為它遵守因子的兩個準則

在Python中可以用列表推導式來代表map操作

Preserves identity - 一致性

li = [1, 2, 3]
[l for l in li] == li
# True
複製程式碼

Composable - 組合性

li = [1, 2, 3]
compose = lambda f, g: lambda a: f(g(a))
[compose(str, lambda x: x+1)(l) for l in li]
# ['2', '3', '4']
[str(l+1) for l in li]
# ['2', '3', '4']
複製程式碼

Referential Transparency - 引用透明性

一個表示式能夠被它的值替代而不改變程式的行為成為引用透明

greet = lambda: 'hello, world.'
複製程式碼

Lazy evaluation - 惰性求值

按需求值機制,只有當需要計算所得值時才會計算

Python中可用生成器實現

import random
def rand():
    while True:
        yield random.random()
rand_iter = rand()
next(rand())
# 0.16066473752585098
複製程式碼

Monoid - 單位半群

一個物件擁有一個函式用來連線相同型別的物件

數值加法是一個簡單的Monoid

1 + 1
# 2
複製程式碼

以上例子中,數值是物件,而+是函式

以下能更清晰地說明它

from operator import add
type(1)
# <class 'int'>
add(1, 1)
# 2
複製程式碼

數值是int類的例項物件,add是實現了加法的函式

與另一個值結合而不會改變它的值必須存在,稱為identity

加法的identity值為 0:

1 + 0
# 1
複製程式碼

需要滿足結合律

1 + (2 + 3) == (1 + 2) + 3
# True
複製程式碼

list的結合也是Monoid

[1, 2].extend([3, 4])
複製程式碼

identity值為空陣列

[1, 2].extend([])
複製程式碼

identity與compose函式能夠組成monoid

identity = lambda a: a
compose = lambda f, g: lambda a: f(g(a))
foo = lambda bar: bar + 1
compose(foo, identity)(1) == compose(identity, foo)(1) == foo(1)
# True
複製程式碼

Monad - 單子

擁有ofchain函式的物件。chain很像map,除了用來鋪平巢狀資料。

flatten = lambda li: sum(li, [])
of = lambda *args: list(args)
chain = lambda func: lambda li: list(flatten([func(l) for l in li]))

[s.split(',') for s in of('cat,dog', 'fish,bird')]
# [['cat', 'dog'], ['fish', 'bird']]

chain(lambda s: s.split(','))(of('cat,dog', 'fish,bird'))
# ['cat', 'dog', 'fish', 'bird']
複製程式碼

Comonad - 餘單子

擁有extractextend函式的物件。

class CoIdentity:
    def __init__(self, v):
        self.val = v
    def extract(self):
        return self.val
    def extend(self, func):
        return CoIdentity(func(self))

CoIdentity(1).extract()
1
from beeprint import pp
pp(CoIdentity(1).extend(lambda x: x.extract() + 1))
# instance(CoIdentity):
#   val: 2
複製程式碼

Morphism - 態射

一個變形的函式

Endomorphism - 自同態

輸入輸出是相同型別的函式

uppercase = lambda string: string.upper()
uppercase('hello')
# 'HELLO'

decrement = lambda number: number - 1
decrement(2)
# 1
複製程式碼

Isomorphism - 同構

不同型別物件的變形,保持結構並且不丟失資料。

例如,一個二維座標既可以表示為列表[2, 3],也可以表示為字典{'x': 2, 'y': 3}

pair_to_coords = lambda pair: {'x': pair[0], 'y': pair[1]}
coords_to_pair = lambda coords: [coords['x'], coords['y']]
pair_to_coords(coords_to_pair({'x': 1, 'y': 2}))
#{'x': 1, 'y': 2}
複製程式碼

Setoid

擁有equals函式的物件。equals可以用來和其它物件比較。

Python裡的==就是equals函式

[1, 2] == [1, 2]
# True

[1, 2] == [3, 4]
# False
複製程式碼

Semigroup - 半群

擁有concat函式的物件。concat可以連線相同型別的兩個物件。

Python裡列表的extend就是concat函式

li = [1]
li.extend([2])
li
# [1, 2]
複製程式碼

Foldable

一個擁有reduce函式的物件。reduce可以把一種型別的物件轉化為另一種型別。

from functools import reduce
sum_ = lambda li: reduce(lambda acc, val: acc + val, li, 0)
sum_([1, 2, 3])
6
複製程式碼

Type Signatures - 型別簽名

通常可以在註釋中指出引數與返回值的型別

# add :: int -> int -> int
add = lambda x: lambda y: x + y

# increment :: int -> int
increment = lambda x: x + 1
複製程式碼

如果函式的引數也是函式,那麼這個函式需要用括號括起來

# call :: (a -> b) -> a -> b
call = lambda func: lambda x: func(x)
複製程式碼

字元a, b, c, d表明引數可以是任意型別。以下版本的map的引數func,把一種型別a的陣列轉化為另一種型別b的陣列

# map :: (a -> b) -> [a] -> [b]
map_ = lambda func: lambda li: [func(l) for l in li]
複製程式碼

常用庫

相關文章