Python_learning --- Functional Programming

Python_learning --- Functional Programming

Functional Programming(函数式编程)

函数式编程是一种编程基于函数的编程风格(顾名思义)

函数式编程的一个主要部分就是高度有序的函数 。我们已经在前面把函数作为对象的章节看到一些关于这部分的内容。高度有序的函数采用另外的函数作为声明,或者是以其作为结果来返回函数。

示例一:

def apply_twice(func,arg):
    return func(func(arg))

def add_five(x):
    return x+5

print(apply_twice(add_five,10))

Result:

20

==Tips== 函数apply_twice 采用另外的函数作为声明,并在其函数内部调用了两次。

示例二:

def test(func,arg):
    return func(func(arg))

def mult(x):
    return x*x

print(test(mult,2))

Result:

4

纯函数

函数式编程寻求使用纯函数。纯函数没有负面作用,但是返回的值仅仅依赖于它们的参数。

这就是函数叜数学中的工作原理:例如,cos(x)对于相同的x值,就总是只会返回同样的结果。

下面是关于纯函数和非纯函数的例子:

纯函数(示例一)

def pure_function(x,y):
    temp=x+2*y
    return temp/(2*x+y)

非纯函数 (示例二)

some_list=[]
def impure(arg):
    some_list.append(arg)
impure(3)
print(some_list)

==Tips== 上面的函数明显是不纯的,因为它改变了some_list 的状态。append 函数无法返回值,只会修改原来的列表

示例三:

def func(x):
    y=x**2
    z=x+y
    return(z)

使用纯函数有优点也有不足。

纯函数:

  • 更容易进行推理和测试
  • 更有效。一旦为输入的值计算了函数,结果将会被存储并在下一次需要该输入的函数时引用它,减少了函数被调用的次数,这叫做记忆法。
  • 更容易进行平行运算

==Notice== 仅仅使用纯函数的劣势在于它们会使的原本很简单的I/O任务变得复杂,因为它似乎原本就要求产生一些负面影响。而且在一些情况下,它们也会变得更加难写。

Lambdas(匿名函数)

正常情况下创建一个函数会将其自动赋值给变量。这不同于其他对象的创建-例如字符串和整数-它们可以被快速创建而不会被赋值给变量。函数也可以这样做,前体是要使用lambda 语法来创建。用这种方式创建的函数是匿名的。当将一个简单的函数作为另外一个函数的参数时,通常就会用到这种方法。这个语法被展示在下面的示例中,由lambda 关键字,参数列表,逗号,表达值和返回组成。

示例一:

def my_func(f,arg):
    return f(arg)
my_func(lambda x: 2*x*x,5)

==Notice== Lambda 函数从lambda calculus(演算)中获得名字,这是Alonzo Church发明的计算模型

但是,匿名函数的功能没有命名函数的功能强大。它们仅仅能够实现要求单个表达的事情,通常相当于单行密码。

示例一:

#命名函数
def polynomial(x):
    return x**2+5*x+4
print(polynomial(-4))

#匿名函数
print((lambda x:x**2+5*x+4)(-4))

==Notice==:在上面的代码中,我们动态地创建了一个匿名函数并在一个语句中对它进行调用

匿名函数可以被赋值给变量,而且可以像正常函数一样进行使用。

示例一:

double=lambda x:x*2
print(double(7))

==Notice== :然而,很少有较好的理由来这样做--通常使用def 来定义函数会更好。

示例二:

triple=lambda x:x*3
add=lambda x,y:x+y
print(add(triple(3),4))

示例三:

a=(lambda x:x*(x+1)) (6)
print(a)

Result:

30

map and filter

map:内置的函数mapfilter 是用来对列表(或者叫做可迭代对象的类似的对象)进行操作的非常有用的高度有序的函数。map 函数一般将一个函数以及一个可迭代对象作为参数,然后返回一个新的可迭代对象,这个对象的每个声明都采用函数处理过。

示例一:

def add_five(x):
    return x+5

nums=[11,22,33,44,55]
result=list(map(add_five,nums))
print(result)

Result:

[16,27,38,49,60]

我们已经通过使用lambda 语法来更容易地实现同样的结果

示例二:

nums=[11,22,33,44,55]
result=list(map(lambda x:x+5,nums))
print(result)

==Notice== :为了将结果转换成一个列表,我们使用了list ,当然元组也可以进行类似的操作,但是最后为了得到元组对象,可以使用函数tuple

示例三:

nums=[11,22,33,44]
a=list(map(lambda x:x*2 nums))

filter: 函数filter 可以对一个可迭代对戏进行筛选,通过移除不满足预期的元素(实质上是一个返回一个布尔值的函数)

示例一:

nums=[11,22,33,44,55]
res=list(filter(lambda x:x%2==0,nums))
print(res)

Result:

[22,44]

==Notice== 和map 一样,如果你想输出一个列表的话,结果就必须明确地指定转换成一个列表

示例二:

nums=[1,2,3,4,0,7,6]
res=list(filter(lambda x:x<5,nums))
print(res)

Result:

[1,2,3,4,0]

示例三:

nums={1,2,3,4,5,6}
nums={0,1,2,3} & nums
nums=filter(lambda x:x>1,nums)
print(len(list(nums)))

Result:

2

示例四:

nums=[1,2,8,3,7]
res=list(filter(lambda x:x%2==0,nums))
print(res)

Result:

[2,8]

Generators(生成器)

生成器是一种可迭代对象,与列表和元组类似。

与列表不一样,它们不允许随机索引来进行检索,但是它们仍然可以通过循环来进行迭代。生成器可以使用函数和yield 语句来创建。

示例一:

def countdown():
    i=5
    while i>0:
        yield i
        i-=1
for i in countdown():
    print(i)

Result:

5
4
3
2
1

==Notice==yield 语句被用来定义一个生成器,代替了一个函数的返回值来吧结果提供给它的调用者而没有损坏本地变量。

实际上,由于每次只产生一个元素,所有生成器并没有列表的缓存限制。也就是说事实上它们是无限的。

示例一:

def infinite_sevens():
    while Trun:
        yield 7
for i in infinite_sevens():
    print(i)

Result:

7
7
......

==Notice== : 总结,生成器可以使你声明一个像迭代器一样进行运行的函数,也就是说,它可以被用在一个循环中。

示例二:

def is_prime(x):
    if x==1:
        return False
    elif x==2 or x==3:
        return True
    for i in range(2,int(sqrt(x))+1):
         if x%i=0:
               return False
          else:
               return True
    
def get_primes():
    num=2
    while True:
        if is_prime(num):
            yield num
         num+=1
for i in get_primes():
    print(i)

有限的生成器可以通过将它们传递给list 作为参数从而被转变为列表

def numbers(x):
    for i in range(x):
        if i%2==0:
            yield i
print(list(numbers(11)))                 

Result:

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

==Notice== : 使用生成器会提高执行运行效率,这是延迟生成值(按需)的结果,这通常会产生较低的内存占用。更多地,我们并不需要等到所有的元素都生成了才使用它们。

示例二:

def make_word():
    word=""
    for ch in "spam":
        word +=ch
        yield word
print(list(make_word()))

Result:

['s','sp','spa','spam']

Decorators(装饰器)

Decorators 可以通过调用其他函数来对函数进行修饰。这是非常理想的方式,当你需要去拓展函数的功能但是你又不想自己对它进行修饰的时候。

示例一:

def decor(func):
    def wrap():
        print("==========")
        func()
        print("==========")
    return warp

def print_text():
    print("Hello world")

decorated=decor(print_text)
decorated()

Result:

==========
Hello world
==========

我们定义了一个叫做docor 的函数,这个函数有一个单独的参数func .在decor 的内部,我们定义了一个叫做wrap 的函数。wrap 函数将会打印出一串字符,然后对func() 进行声明,并打印出另外一串字符。decor 函数返回的是wrap 函数作为其结果。

我们可以说被修饰的变量是print_text 的修饰版本-也就是print_text 加上某些东西。

事实上,如果我们写了一个非常有用的修饰器,我们希望可以使用装饰过的版本来替换print_text ,这样我们就总是可以得到print_text 被加上了某些东西的版本。

这可以通过对包含我们的函数的变量进行重新指定来实现。

示例二:

def decor(func):
    def wrap():
        print("==========")
        func()
        print("==========")
    return wrap
def print_text():
    print("Hello world!")
print_text=decor(print_text)
print_text()

==Notice== 现在print_text 对应于我们修饰过的版本,其本质上是对print_text 进行修饰

在我们之前的列子中,我们通过使用一个包含过的版本来将包含有函数的变量替换掉从而对我们的函数进行修饰。

def decor(func):
    def wrap():
        print("----------")
        func()
        print("**********")
     return wrap
def print_text():
    print("Hello world!")
print_text=decor(print_text)

这个模式可以在任何时间使用,来对函数进行包装。Python通过使用修饰符名称和@符号预先挂起函数定义,提供了在装饰器中包装函数的支持。如果我们正在定义一个函数我们可以使用@符号来对它进行修饰。

def decor(func):
    def wrap():
        print("=========")
        func()
        print("=========")
     return wrap
@decor
def print_text():
    print("Hello world")
print_text();

==Notice== 一个单独的函数可以有很多的修饰器。

这和上面产生的结果是一样的

Recursion(递归)

递归是函数式编程中一个非常重要的概念。递归最基本的部分就是自引用-也就是函数自己调用自己:它可以用来解决可以被分解为更简单的而且是同一类型的子问题。

一个典型的可以被进行递归的函数例子就是factorial 函数,它可以找到所有低于某一特定数字的正数的结果。

例如,5!(阶乘)是5*4*3*2*1(120)。为了实现递归,我们应该注意到5!=5*4!, 4!=4*3!, 3!=3*2!,等等。通常,n!=n*(n-1)!。此外, 1!=1,这就是我们所知的基本情况,因为它可以在不执行更多阶乘的时候进行计算。下面是阶乘函数的递归实现。

def factorial(x):
    if x == 1:
        return 1
    else:
        return x*factorial(x-1)

print(factorial(5))    

==Notice== 基本情况可以作为递归退出时的情况

递归函数可以无限循环,就行无限的while 循环一样。当你忘记执行基本情况的时候就会发生无限循环。

下面是阶乘函数不正确的实现方式。它没有基本情况,所以它一直运行指导编辑器的内存占满而且崩溃的时候才会停止运行。

示例一:

def factorial(x):
    return x*factorial(x-1)
print(factorial(5))

Result:

RecursionError: maximum recursion depth exceeded

示例二:

def sum_to(x):
    return x+sum_to(x-1)
print(sum_to(5))

==Notice== 上面的这个代码同样没有设置基本情况,所以会无限循环。

修正:

def sum_to(x):
    if x==0:
        return 0
    else:
        return x+sum_to(x-1)
print(sum_to(5))   

Result:

15

示例三:

def power(x,y):
    if y==0:
        return 1
    else:
        return x*power(x,y-1)
print(power(2,3))

Result:

8

递归也可以是间接的。一个函数可以可以调用第二个函数,第二个函数可以调用第一个函数,第二个函数调用第二个函数,等等以此类推。这通常会发生在有很多函数的时候。

示例一:

def is_even(x):
    if x==0:
        return True
    else:
        return is_odd(x-1)
def is_odd(x):
    return not is_even(x)

print(is_odd(7))
print(is_even(23))
   
    

Result:

True
False

示例二:

def fib(x):
    if x==0 or x==1:
        return 1
    else:
        return fib(x-1)+fib(x-2)
print(fib(4))

Sets(集合)

集合是一种数据结构,类似于列表和辞典。它们可以使用huak花括号来创建,或者set 函数来创建。它们与列表共享一些功能,例如可以使用in 来检查其中是否包含有一个特定的元素。

示例一:

num_set={1,2,3,4,5}
word_set=set(["spam","eggs","sausage"])

print(3 in num_set)
print("spam" not in word_set)

Result:

True
False

==Notice== 为了创建一个空的集合,你必须使用set() ,因为{}创建的是一个空的辞典。

示例二:

letters={"a","b","c","d"}
if "e" not in letters:
    print(1)
else:
    print(2)

集合与列表有很多不同之处,但是也与列表共享一些操作,比如len.

它们是无序的,这也就意味着它们不可用被进行索引。

它们不可以包含重复的元素。

由于它们的存储方式,可以较快地检查是否某个元素是集合的一部分,而不是列表的一部分。

对集合进行添加不是使用append 而是add

remove 方法可以从一个集合中移除一个特定的元素,pop 则移除的是任意的元素。

示例一:

nums={1,2,1,3,14,5,6}
print(nums)
nums.add(-7)
nums.remove(3)
print(nums)

Result:

{1,2,3,4,5,6}
{1,2,4,5,6,-7}

==Notice== 集合的基本使用包括成员测试和重复条目的删除

示例二:

nums={"a","b","c","d"}
nums.add("x")
print(len(nums))

集合可以使用数学运算来进行组合。

合并操作符| 可以将两个集合合并成一个新的包含之前两个集合中所有元素的条目。

交叉操作符& 可以获得只有在两个集合中都存在的元素。

差异操作符- 获得在第一个集合中存在而不存在与第二个集合中的元素。

对称差异操作符^ 获得那些不是在两个集合中都有的两个集合中的项。

first={1,2,3,4,5,6}
second={4,5,6,7,8,9}

print(first | second)
print(first & second)
print(first - second)
print(second - first)
print(first ^ second)

Result:

{1, 2, 3, 4, 5, 6, 7, 8, 9}
{4, 5, 6}
{1, 2, 3}
{8, 9, 7}
{1, 2, 3, 7, 8, 9}

数据结构

正如我们在之前的部分所看到的,Python支持以下几种数据结构:列表(list),辞典(dictionaries), 元组(tuples), 集合(sets)

什么时候使用辞典(dictionaries):

  • 当你需要key:value 配对之间的逻辑关系时
  • 当你需要基于某一自定义的键来快速查看数据的时候
  • 当你的数据被连续进行修饰时。记住,辞典是可变的

什么时候使用其他类型的数据:

  • 如果有一组不需要进行随机访问的数据,请选择列表。试着选择列表。

当你需要一个简单的,可以迭代的,可以进行经常修饰的集合的时候

  • 如果你需要元素的唯一性,请使用集合
  • 当你的数据不能被进行改变的时候,使用元组

==Notice== 很多时候,元组可以与辞典结合使用,例如,一个元组可以作为一个键,因为元组是不能改变的。

iterools(循环器)

itertools 模块是一个标准的库,包含有一些在函数式编程中非常有用的函数。

它生成的一种函数是无限迭代器。

函数count 从一个值开始进行无限累加。

函数cycle 通过一个迭代器进行无限循环(例如,一个列表或者一串字符串)。

函数repeat 对一个对象进行重复,要么是无限地,要么是特定数字指定的次数。

示例一:

from itertools import count
for i in count(3):
    print(i)
    if i>=11:
        break

itertools 里面有很多的函数可以进行迭代,与mapfilter 的操作类型。

一些列子:

takewhile -当一个预测的函数保持为TRUE的时候,从可迭代对象中取出项。

chain -将很多可迭代对象合并为很长的一个

accumulate -返回一个可迭代值中的全部运行结果

示例一:

from itertools import accumulate,takewhile
nums=list(accumulate(range(8)))
print(nums)
print(list(takewhile(lambda x:x<=6,nums)))

Result:

[0,1,3,6,10,15,21,28]
[0,1,3,6]

示例二:

from itertools import takewhile
nums=[2,4,6,7,9,8]
a=takewhile(lambda x:x%2==0,nums)
print(list(a))

Result:

[2,4,6]

==Notice== takewhile 遇到不满足条件的项时,就停止执行了,所有之后即使再有符合条件的项,也不会输出了

itertools 里面同样包含很多组合函数,例如productpermutation

当你想用一些项的所有的组合来完成任务的时候,就可以使用这些函数。

示例一:

from itertools import product,permulations

letters=("A","B")
print(list(product(letters,range(2))))
print(list(permulation(letters)))

Result:

[('A', 0), ('A', 1), ('B', 0), ('B', 1)]
[('A', 'B'), ('B', 'A')]

==Notice== product 函数能够将两个迭代对象中的项进行所有可能的组合形式。permutation 函数则可以列出迭代对象中所有可能的排列形式。

示例二:

from itertools import product
a={1,2}
print(len(list(product(range(3),a))))

Result:g

6

你可能感兴趣的:(Python_learning --- Functional Programming)