fppy
开发状态:

依赖:

一个基于python
的函数式编程类库,仅做学习使用。主要功能如下:
如何安装
从PYPI软件库:
pip install fppy-learn
下载源码自己安装:
pip install poetry
git clone https://github.com/threecifanggen/python-functional-programming.git
cd python-functional-programming
poetry install
快速功能预览
函数修饰器
from fppy.base import F_
@F_
def f(x):
return x + 1
>>> f(1)
2
>>> f.apply(1)
2
>>> f.and_then(lambda x: x ** 3)(1)
8
>>> f.compose(lambda x: x ** 3)(1)
1
>>> f.map([1, 2, 3])
[2, 3, 4]
常量定义
>>> from fppy.const import Const
>>>
>>> const = Const()
>>> const.a = 1
>>> const.a
1
>>> const.a = 2
列表类
一个可以实现map
、reduce
等操作的惰性列表
元组实现的列表
1. 新建
from fppy.cons_list_base import *
a = cons_apply(1, 2, 3)
head(a)
tail(a)
2. 打印
>>> print_cons(cons_apply(1, 2, 3))
1, 2, 3, nil
3. 列表操作
a = cons_apply(1, 2, 3)
map_cons_curry(lambda x: x + 1)(a)
filter_cons_curry(lambda x: x % 2 == 0)(a)
fold_left_cons_curry(lambda x, y: x + y)(0)(a)
类实现的列表
from fppy.cons_list import Cons
Cons.maker(1, 2, 3)\
.map(lambda x: x + 1)\
.filter(lambda x: x % 2 == 0)\
.fold_left(lambda x, y: x + y, 0)
从头实现的惰性列表
from fppy.lazy_list_base import LazyCons
LazyCons.from_iter(1)(lambda x: x)\
.map(lambda x: x + 1)\
.filter(lambda x: x % 2 == 0)\
.take(3)\
.fold_left(lambda x, y: x + y, 0)
惰性列表
1. 新建
from fppy.lazy_list import LazyList
ll = LazyList.from_iter(2)(lambda x: x + 2)
ll = LazyList([1, 2, 3])
x = (i for i in range(100))
ll = LazyList(x)
2. map、filter、collect
LazyList([1, 2, 3])\
.map(lambda x: x + 1)\
.filter(lambda x: x % 2 == 0)\
.collect()
3. 其他
其他方法参考文档。
常见组合子
1. Y组合子
下面的例子是计算阶乘:
from fppy.combinator import Y
fac = Y(lambd f: lambda x: 1 if (x ==0) else x * f(x - 1))
2. Z组合子
下面是计算指数函数的Z组合子实现
from fppy.combinator import Z
power = Z(lambda f: lambda x, n: 1 if (n == 0) else x * f(x, n - 1))
偏函数
这里的偏函数是指Partial Function,即定义域取不完整的函数;而不是高阶函数中的Partial Applied Function的概念。
定义一个如下函数:
- 如果
x > 0
,则计算1 / x
- 如果
x < 0
,则计算log(-x)
from math import log
from fppy.partail_function import PartialFunction
pf = PartialFunction\
.case(lambda x: x > 0)\
.then(lambda x: 1 / x)\
.case(lambda x: x < 0)\
.then(lambda x: log(-x))
pf.apply(1)
pf.apply(-1)
pf.apply(0)
pf.is_defined_at(0.4)
pf.is_defined_at(0)
我们还可以使用or_else
来组合偏函数,比如上面的函数可以如下实现:
pf_greater_then_0 = PartialFunction\
.case(lambda x: x > 0)\
.then(lambda x: 1 / x)
pf_less_then_0 = PartialFunction\
.case(lambda x: x < 0)\
.then(lambda x: log(-x))
pf = pf_greater_then_0.or_else(pf_less_then_0)
惰性求值
1. 惰性属性
from fppy.lazy_evaluate import lazy_property
@dataclass
class Circle:
x: float
y: float
r: float
@lazy_property
def area(self):
print("area compute")
return self.r ** 2 * 3.14
以上定义了一个圆的类,具体获取area
时,仅第一次会计算(通过打印"area compute"
显示)。
2. 惰性值
Python
没有代码块的概念,所以必须把惰性求值过程包在一个函数内,以下是调用方法:
from fppy.lazy_evaluate import lazy_val
def f():
print("f compute")
return 12
lazy_val.a = f
调用结果下:
>>> lazy_val.a
f compute
12
>>> lazy_val.a
12
这就表示仅第一次调用时发生了计算。
错误处理
1. Option
from fppy.option import Just, Nothing
>>> Just(1).map(lambda x: x + 1)
Just(2)
>>> Just(1).flat_map(lambda x: Just(x * 3))
Just(3)
>>> Just(1).get
1
>>> Just(1).get_or_else(2)
1
>>> Just(1).filter(lambda x: x < 0)
Nothing()
>>> Just(1).filter(lambda x: x > 0)
Just(1)
与偏函数合用会有很多妙处:
from math import log
from fppy.partail_function import PartialFunction
pf = PartialFunction\
.case(lambda x: x > 0)\
.then(lambda x: 1 / x)\
.case(lambda x: x < 0)\
.then(lambda x: log(-x))
>>> pf.lift(1)
Just(1)
>>> pf.lift(0)
Nothing()
>>> Just(1).collect(pf)
Just(1)
>>> Just(0).collect(pf)
Nothing()
>>> Just(1).collect(pf)
Just(1.)
>>> Just(1).collect(pf).map(lambda x: int(x) - 1)
Just(-1)
>>> Just(1).collect(pf).map(lambda x: int(x) - 1).collect(pf)
Just(0)
>>> Just(1).collect(pf).map(lambda x: int(x) - 1).collect(pf).collect(pf)
Nothing()
>>> Just(1).collect(pf).map(lambda x: int(x) - 1).collect(pf).collect(pf).collect(pf)
Nothing()
>>> Just(1).collect(pf).map(lambda x: int(x) - 1).collect(pf).collect(pf).collect(pf).get_or_else(2)
2
2. Either
(待完善)
3. Try
Try
单子时一个非常方便地处理错误的类,它的逻辑时传递错误类一直到最后处理,可以获取到错误发生时的错误类型和输入值,方便调试:
>>> from fppy.try_monad import Try
>>> res = Try(1).map(lambda x: x / 0).map(lambda x: x + 1)
>>> res.error
ZeroDivisionError('division by zero')
>>> res.get_or_else(2)
2
>>> res.get_error_input()
1