首页
登录 | 注册

函数式编程和面向对象的编程对比与格式化字符串

     函数式编程是一种代码重用策略 ,编写的代码代码安全,可靠,可重用!

     主要原则:使用的函数没有副作用,即函数的输出完全取决于输入,且不修改外部的任何东西。

     主要特征:函数本身就是对象,可以将其作为参数传递给其它函数,还可以存储在变量中。

     而在面向对象编程中,面向对象程序设计(英语: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中的函数对象了。接下来我们来探讨实现惰性求值的关键技术。














2020 jeepxie.net webmaster#jeepxie.net
10 q. 0.007 s.
京ICP备10005923号