函数式编程是一种代码重用策略 ,编写的代码代码安全,可靠,可重用!
主要原则:使用的函数没有副作用,即函数的输出完全取决于输入,且不修改外部的任何东西。
主要特征:函数本身就是对象,可以将其作为参数传递给其它函数,还可以存储在变量中。
而在面向对象编程中,面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
对象与对象之间的关系是面向对象编程首要考虑的问题,而在函数式编程中,所有的数据都是不可变的,不同的函数之间通过数据流来交换信息,函数作为FP中的一等公民,享有跟数据一样的地位,可以作为参数传递给下一个函数,同时也可以作为返回值。
函数式编程在python中的实现:装饰器,装饰器是一个函数,用来给其它类或者方法添加新的功能,如日志打印等!
def decorator(func):
def inner_function():
pass
return inner_function
@decorator
def func():
pass
print(func.__name__)
# 输出: inner_function
python中另一个很有用的功能就是格式化字符串!
python中format函数用于字符串的格式化
通过关键字
1 print('{名字}今天{动作}'.format(名字='陈某某',动作='拍视频'))#通过关键字 2 grade = {'name' : '陈某某', 'fenshu': '59'} 3 print('{name}电工考了{fenshu}'.format(**grade))#通过关键字,可用字典当关键字传入值时,在字典前加**即可
通过位置
1 print('{1}今天{0}'.format('拍视频','陈某某'))#通过位置 2 print('{0}今天{1}'.format('陈某某','拍视频'))
填充和对齐^<>分别表示居中、左对齐、右对齐,后面带宽度
1 print('{:^14}'.format('陈某某')) 2 print('{:>14}'.format('陈某某')) 3 print('{:<14}'.format('陈某某')) 4 print('{:*<14}'.format('陈某某')) 5 print('{:&>14}'.format('陈某某'))#填充和对齐^<>分别表示居中、左对齐、右对齐,后面带宽度
精度和类型f精度常和f一起使用
1 print('{:.1f}'.format(4.234324525254)) 2 print('{:.4f}'.format(4.1))
进制转化,b o d x 分别表示二、八、十、十六进制
print('{:b}'.format(250)) print('{:o}'.format(250)) print('{:d}'.format(250)) print('{:x}'.format(250))
千分位分隔符,这种情况只针对与数字
print('{:,}'.format(100000000)) print('{:,}'.format(235445.234235))
c++中函数式编程
函数式编程是一种编程范式,它有下面的一些特征:
函数是一等公民,可以像数据一样传来传去。
高阶函数
递归
pipeline
惰性求值
柯里化
偏应用函数
C++98/03中的函数对象,和C++11中的Lambda表达式、std::function和std::bind让C++的函数式编程变得容易。我们可以利用C++11/14里的新特性来实现高阶函数、链式调用、惰性求值和柯理化等函数式编程特性。本文将通过一些典型示例来讲解如何使用现代C++来实现函数式编程。
高阶函数和pipeline的表现形式
高阶函数就是参数为函数或返回值为函数的函数,经典的高阶函数就是map、filter、fold和compose函数,比如Scala中高阶函数:
map
numbers.map((i: Int) => i * 2)
对列表中的每个元素应用一个函数,返回应用后的元素所组成的列表。
filter
numbers.filter((i: Int) => i % 2 == 0)
移除任何对传入函数计算结果为false的元素。
fold
numbers.fold(0) { (z, i) =>
a + i
}
将一个初始值和一个二元函数的结果累加起来。
compose
val fComposeG = f _ compose g _
fComposeG("x")
组合其它函数形成一个新函数f(g(x))。
上面的例子中,有的是参数为函数,有的是参数和返回值都是函数。高阶函数不仅语义上更加抽象泛化,还能实现“函数是一等公民”,将函数像data一样传来传去或者组合,非常灵活。其中,compose还可以实现惰性求值,compose的返回结果是一个函数,我们可以保存起来,在后面需要的时候调用。
pipeline把一组函数放到一个数组或是列表中,然后把数据传给这个列表。数据就像一个链条一样顺序地被各个函数所操作,最终得到我们想要的结果。它的设计哲学就是让每个功能就做一件事,并把这件事做到极致,软件或程序的拼装会变得更为简单和直观。
Scala中的链式调用是这样的:
s(x) = (1 to x) |> filter (x => x % 2 == 0) |> map (x => x * 2)
用法和Unix Shell的管道操作比较像,|前面的数据或函数作为|后面函数的输入,顺序执行直到最后一个函数。
这种管道方式的函数调用让逻辑看起来更加清晰明了,也非常灵活,允许你将多个高阶函数自由组合成一个链条,同时还可以保存起来实现惰性求值。现代C++实现这种pipeline也是比较容易的,下面来讲解如何充分借助C++11/14的新特性来实现这些高阶函数和pipeline。
实现pipeline的关键技术
根据前面介绍的pipeline表现形式,可以把pipeline分解为几部分:高阶函数,惰性求值,运算符|、柯里化和pipeline,把这几部分实现之后就可以组成一个完整的pipeline了。下面来分别介绍它们的实现技术。
高阶函数
函数式编程的核心就是函数,它是一等公民,最灵活的函数就是高阶函数,现代C++的算法中已经有很多高阶函数了,比如for_each, transform:
std::vector<int> vec{1,2,3,4,5,6,7,8,9}
//接受一个打印的Lambda表达式
std::for_each(vec.begin(), vec.end(), [](auto i){ std::cout<<i<<std::endl; });
//接受一个转换的Lambda表达式
transform(vec.begin(), vec.end(), vec.begin(), [](int i){ return i*i; });
这些高阶函数不仅可以接受Lambda表达式,还能接受std::function、函数对象、普通的全局函数,很灵活。需要注意的是,普通的全局函数在pipeline时存在局限性,因为它不像函数对象一样可以保存起来延迟调用,所以我们需要一个方法将普通的函数转换为函数对象。std::bind也可以将函数转化为函数对象,但是bind不够通用,使用的时候它只能绑定有限的参数,如果函数本身就是可变参数的就无法bind了,所以,这个函数对象必须是泛化的,类似于这样:
class universal_functor
{
public:
template <typename... Args> auto operator()(Args&&... args) const ->decltype(globle_func(std::forward<Args>(args)...))
{
return globle_func(std::forward<Args>(args)...);
}
};
上面的函数对象内部包装了一个普通函数的调用,当函数调用的时候实际上会调用普通函数globle_func,但是这个代码不通用,它无法用于其他的函数。为了让这个转换变得通用,我们可以借助一个宏来实现function到functor的转换。
#define define_functor_type(func_name) class tfn_##func_name {\
public: template <typename... Args> auto operator()(Args&&... args) const ->decltype(func_name(std::forward<Args>(args)...))\
{ return func_name(std::forward<Args>(args)...); } }
//test code
int add(int a, int b)
{
return a + b;
}
int add_one(int a)
{
return 1 + a;
}
define_functor_type(add);
define_functor_type(add_one);
int main()
{
tnf_add add_functor;
add_functor(1, 2); //result is 3
tfn_add_one add_one_functor;
add_one_functor(1); //result is 2
return 0;
}
我们先定义了一个宏,这个宏根据参数来生成一个可变参数的函数对象,这个函数对象的类型名为tfn_加普通函数的函数名,之所以要加一个前缀tfn_,是为了避免类型名和函数名重名。define_functor_type宏只是定义了一个函数对象的类型,用起来略感不便,还可以再简化一下,让使用更方便。我们可以再定义一个宏来生成转换后的函数对象:
#define make_globle_functor(NAME, F) const auto NAME = define_functor_type(F);
//test code
make_globle_functor(fn_add, add);
make_globle_functor(fn_add_one, add_one);
int main()
{
fn_add(1, 2);
fn_add_one(1);
return 0;
}
make_globle_functor生成了一个可以直接使用的全局函数对象,使用起来更方便了。用这个方法就可以将普通函数转成pipeline中的函数对象了。接下来我们来探讨实现惰性求值的关键技术。