澳门新葡萄京官网注册 1

澳门新葡萄京官网注册如何读懂并写出装逼的函数式代码

今天在微博上看到了
有人分享了下面的这段函数式代码,我把代码贴到下面,不过我对原来的代码略有改动,对于函数式的版本,咋一看,的确令人非常费解,仔细看一下,你可能就晕掉了,似乎完全就是天书,看上去非常装逼,哈哈。不过,我感觉解析那段函数式的代码可能会一个比较有趣过程,而且,我以前写过一篇《函数式编程》的入门式的文章,正好可以用这个例子,再升华一下原来的那篇文章,顺便可以向大家更好的介绍很多基础知识,所以写下这篇文章。

简学Python第三章__函数式编程、递归、内置函数,简学python

 

澳门新葡萄京官网注册 1

Python第三章__函数式编程、递归、闭包

 

欢迎加入Linux_Python学习群

 澳门新葡萄京官网注册 2 群号:478616847

 

 

目录:

  • 函数式编程

  • **传参与返回值**

  • 递归

  • 匿名函数**
    **

  • 闭包

  • 高阶函数

  • 内置函数

 

在第三章,我们引入新的概念函数,在以往的代码编写中我们都是用的过程式编程,函数式编程的特点将过程式编程变成易于管理调用的小模块,

让重复的代码可以反复的调用,大大减少代码量,懒惰即美德

 

一,函数的基本介绍

首先谈到函数,相信大家都不陌生,不管是其他语言都会用到,可能就是叫法不一样。就我知道的跟python中函数类似的,在C中只有function,在Java里面叫做method,在js中也是叫function。函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

函数有下面三点好处:

1.能够减少重复代码的使用

2.让你的程序有更好的扩展性

3.可以让你的程序变得更加容易维护

下面我们就来讲一下怎么定义一个函数

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()
  • 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式]
    结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回
    None。

补充知识:return不返回值的时候有三种方法:不写return,只写一个return,或者return
None。return当有多个返回值的时候用逗号分隔。接收的时候可以用一个变量接收(元组),也可以用多个变量接收。

先看代码

这个代码平淡无奇,就是从一个数组中找到一个数,O(n)的算法,找不到就返回
null。

下面是正常的 old-school 的方式。不用多说。

//正常的版本
function find (x, y) {
  for ( let i = 0; i < x.length; i++ ) {
    if ( x[i] == y ) return i;
  }
  return null;
}

let arr = [0,1,2,3,4,5]
console.log(find(arr, 2))
console.log(find(arr, 8))

结果到了函数式成了下面这个样子(好像上面的那些代码在下面若影若现,不过又有点不太一样,为了消掉if语言,让其看上去更像一个表达式,动用了
? 号表达式):

//函数式的版本
const find = ( f => f(f) ) ( f =>
  (next => (x, y, i = 0) =>
    ( i >= x.length) ?  null :
      ( x[i] == y ) ? i :
        next(x, y, i+1))((...args) =>
          (f(f))(...args)))

let arr = [0,1,2,3,4,5]
console.log(find(arr, 2))
console.log(find(arr, 8))

为了讲清这个代码,需要先补充一些知识。

一、函数式编程

  创建函数

  一个函数式由关键字 def
,与函数名与括号冒号,和括号中的参数组成,当想要执行函数的时候只需要写上函数名加括号即可

  格式:  def  function
(parameter)  下面就创建一个函数

澳门新葡萄京官网注册 3

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 #创建函数
5 def print_str():
6     print("Hello World")
7 
8 #调用函数
9 print_str()

第一个函数

  变量的作用域

  我们看一个例子,在下面代码中我们先把变量 a=“haha” 然后在函数中把
a=“heihei” 最后执行这个函数,并输出变量a的结果

  我们发现为什么a不等于 “heihei”,而是我们之前赋值的 “haha”
这个现象我们马上揭秘

澳门新葡萄京官网注册 4

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 a = "haha"
 6 
 7 #创建函数
 8 def print_str():
 9     a = "heihei"
10     print("Hello World")
11 
12 #调用函数
13 print_str()
14 print("我是变量a:",a)

变量的作用域例一

  全局变量与局部变量

  很明显变量的作用域就是变量在哪一个范围内生效,如果出了这个范围则无法使用

  全局变量:通常全局变量存活在脚本运行结束,并且所有的函数都可以访问它

  局部变量:只能局部变量所在的函数内部调用,并且除非把局部变量声明成全局变量否则,其他函数均无法调用,并且局部变量

  当所在的函数执行完成后就不存在了

澳门新葡萄京官网注册 5

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 
 5 a = "haha"
 6 
 7 #创建函数
 8 def print_str():
 9     a = "heihei"
10     print("我是局部变量a:",a)
11 
12 
13 #调用函数
14 print_str()
15 print("我是全局变量a:",a)

全局变量与局部变量

  global

  global就是可以把局部变量变成全局变量的,如果被声明的局部变量名与全局变量名一样的话,那么局部变量会覆盖全局变量,切

  使用global声明变量需要在变量之前声明否则python会告诉你,你需要在
a 前面声明

  报错提示:SyntaxWarning: name ‘a’ is assigned to before global
declaration global a

澳门新葡萄京官网注册 6

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 a = "haha"
 5 
 6 #创建函数
 7 def print_str():
 8     global a
 9     a = "heihei"
10     print("我是局部变量a:",a)
11 
12 #调用函数
13 print_str()
14 print("我是全局变量a:",a)

gloable

 

二,函数的参数

函数可以传参,也可以不传参,都是可以的哦。

澳门新葡萄京官网注册 7澳门新葡萄京官网注册 8

 1 #定义一个需要传参的函数
 2 def say(name):
 3     print('hello',name)
 4 say('zzq')   #调用say这个函数,并且传入参数
 5 
 6 #不需要传参的函数
 7 def take():
 8     print('I Love You')
 9 take()   #调用函数,不传任何参数
10 
11 #有返回值的函数
12 def add(x,y):
13     return x+y
14 a=add(4,5) #传入的参数必须是相同数据类型
15 print(a)   #执行结果为9

View Code

值得注意的是在传入的参数还有三种:普通参数,不定长参数,自典型

澳门新葡萄京官网注册 9澳门新葡萄京官网注册 10

 1 #普通参数
 2 def say1(name):
 3     print(name)
 4 say1('gmx')
 5 
 6 #不定长参数
 7 def say2(*args):
 8     print(args)
 9 say2(1,5,'sd')
10 
11 #自典型
12 def say3(**kwargs):
13     print(kwargs)
14 say3(name='zzq',say='I Love You',to_name='gmx')#传入的参数必须是字典
15 
16 #执行结果为
17 #gmx
18 #(1, 5, 'sd')
19 #{'name': 'zzq', 'say': 'I Love You', 'to_name': 'gmx'}

View Code

听到参数大家还经常听到形参跟实参这两个词,下面我跟大家大概讲一下两者的区别。

  • 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
  • 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值

Javascript的箭头函数

首先先简单说明一下,ECMAScript2015
引入的箭头表达式。箭头函数其实都是匿名函数,其基本语法如下:

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression
     // 等于 :  => { return expression; } 

// 只有一个参数时,括号才可以不加:
(singleParam) => { statements }
singleParam => { statements }

//如果没有参数,就一定要加括号:
() => { statements }

下面是一些示例:

var simple = a => a > 15 ? 15 : a; 
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b);  // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2);       // [10, 12, 26, 0, 2, 36, 46]

看上去不复杂吧。不过,上面前两个 simple 和 max
的例子都把这箭头函数赋值给了一个变量,于是它就有了一个名字。有时候,某些函数在声明的时候就是调用的时候,尤其是函数式编程中,一个函数还对外返回函数的时候。比如下在这个例子:

function MakePowerFn(power) {
  return function PowerFn(base) {
    return Math.pow(base, power);
  } 
}

power3 = MakePowerFn(3); //制造一个X的3次方的函数
power2 = MakePowerFn(2); //制造一个X的2次方的函数

console.log(power3(10)); //10的3次方 = 1000
console.log(power2(10)); //10的2次方 = 100

其实,在 MakePowerFn 函数里的那个 PowerFn 根本不需要命名,完全可以写成:

function MakePowerFn(power) {
  return function(base) {
    return Math.pow(base, power);
  } 
}

如果用箭头函数,可以写成:

MakePowerFn = power  => {
  return base => {
    return Math.pow(base, power);
  } 
}

我们还可以写得更简洁(如果用表达式的话,就不需要 { 和 }, 以及 return
语句 ):

MakePowerFn = power => base => Math.pow(base, power)

我还是加上括号,和换行可能会更清楚一些:

MakePowerFn = (power) => (
  (base) => (Math.pow(base, power))
)

好了,有了上面的知识,我们就可以进入一个更高级的话题——匿名函数的递归。

二、传参与返回值

**  传参**

  函数用起来比较简单也容易理解,但是参数的变化就比较多了,在函数括号内就是参数,参数可以接收字符串,数字,也可以接收字典和列表

  并且在调用的时候,我们还可以指定给哪个参数赋什么值

澳门新葡萄京官网注册 11

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 #创建函数
 5 def print_str(Name,Age,Aender):
 6     print('''
 7     Name:%s
 8     Age:%s
 9     Aender:%s
10     '''%(Name,Age,Aender))
11 
12 #用户输入
13 in_name = input("请输入你的名字:")
14 in_age = input("请输入你的年龄:")
15 in_aender = input("请输入你的性别:")
16 
17 #固定位置传参
18 print_str(in_name,in_age,in_aender)
19 print("----------------------------------")
20 
21 #不固定位置传参
22 print_str(in_name,Aender=in_aender,Age=in_age)

传参

  当我们想要传入列表或者字典时需要怎么办?

澳门新葡萄京官网注册 12

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 def print_str(lists):
 5     if type(lists) == list:
 6         print('''
 7         Name:%s
 8         Age:%s
 9         Aender:%s
10         '''%(lists[0],lists[1],lists[2]))
11     else:
12         print('''
13         Name:%s
14         Age:%s
15         Aender:%s
16         '''%(lists["name"],lists["age"],lists["aenber"]))
17 
18 #传入列表
19 userlist = ["Ben","22","Man"]
20 print_str(userlist)
21 
22 print("----------------------------------")
23 #传入字典
24 userdict = {"name":"Ben","age":"022","aender":"Man"}
25 print_str(userlist)

传入字典或列表

  默认参数

  在函数中还可以设置默认参数,默认参数的意思是这个参数你可以传值也可以不传值,当不传值的时候这个参数就等于默认值

澳门新葡萄京官网注册 13

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 def print_str(lists,Country="China"):
 5     if type(lists) == list:
 6         print('''
 7         Name:%s
 8         Age:%s
 9         Aender:%s
10         country:%s
11         '''%(lists[0],lists[1],lists[2],Country))
12     else:
13         print('''
14         Name:%s
15         Age:%s
16         Aender:%s
17         country:%s
18         '''%(lists["name"],lists["age"],lists["aenber"],Country))
19 
20 #传入列表
21 userlist = ["Ben","22","Man"]
22 print_str(userlist)
23 
24 print("----------------------------------")
25 #传入字典
26 userdict = {"name":"Ben","age":"022","aender":"Man"}
27 print_str(userlist,"America")

默认参数

  非固定参数

  非固定参数的意义在于可以接收任意个值,在你的函数不确定用户想传入多少个值的时候使用,当然在调用有只有非固定参数的函数的时候我们可以不传参数。

  非固定参数分一个*和两个*,*args会把传入的参数变成元祖,**kwargs把传入的参数变成字典,当然*ages可以是别的名称,但是在规范中最好使用*args,和**kwargs

澳门新葡萄京官网注册 14

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 def print_str(*args,**kwargs):
 5     print("我是*",args)
 6     print("我是**",kwargs)
 7 
 8 #传入列表
 9 userlist = ["Ben","22","Man"]
10 print_str(userlist,"America")
11 print("----------------------------------")
12 #传入字典
13 print_str(A = "1",B = "2")

非固定传参

  既然形参可以带*和**,那么实参也是可以带*和**,那么*就是配合列表使用的,**就是配合字典的!

澳门新葡萄京官网注册 15

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 def print_str(*args,**kwargs):
 5     print("我是*",args)
 6     print("我是**",kwargs)
 7 
 8 #传入列表
 9 userlist = ["Ben","22","Man"]
10 print_str(*userlist)
11 
12 print("----------------分隔符----------------")
13 
14 #传入字典
15 userdict = {"name":"Ben","age":"022","gender":"Man"}
16 print_str(**userdict)
17 
18 解参

解参

  实参带*就会把列表分解成   “Ben”,”22″,”Man”
一个一个单独的元素传入函数,而**会把字典分解成name=”Ben”,age=”022″,gender=”Man”

  这种键值对传入函数。

  

  形参与实参

  形参:

  变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用

  函数后则不能再使用该形参变量

  实参:

  可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此

  应预先用赋值,输入等办法使参数获得确定值

  

  注!当普通参数和默认参数和非固定参数结合使用的时候,要遵循一个顺序,普通参数在默认参数前面,默认参数在非固定参数前面

  

  返回值

  在正常使用函数的时候,函数是可以把函数内部处理的结果返回给函数调用者的,在没有返回值得函数中会保留None传给函数调用者,返回值可以返回序列等

  在函数执行的时候遇到return函数会停止执行,并返回结果

澳门新葡萄京官网注册 16

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 #创建函数
 5 def print_str(Age):
 6     if int(Age) <= 30:
 7         return "你才%s啊!真年轻"%(Age)
 8     else:
 9         return "你都%s啦!老家伙"%(Age)
10 
11 in_age = input("请输入你的年龄:")
12 word = print_str(in_age)
13 print(word)

return

  

  嵌套函数

  在函数内部也可以写函数,这样就是外层函数套着内侧函数,这种形式称之为嵌套函数,同理因为作用域的关系嵌套函数只能内部调用

  return unction_2(stra)+”我是第二层n” 就等于先 c
= unction_2(stra) 然后 return c+”我是第二层n”

澳门新葡萄京官网注册 17

1 def unction(stra):
2     def unction_2(stra_2):
3         return stra_2+"我是第三层n"
4     return unction_2(stra)+"我是第二层n"
5 
6 r_str = unction("")
7 print(r_str+"我是第一层")

嵌套函数

 

三,全局变量跟局部变量

什么是全局变量跟局部变量呢,根据他们的名字可以知道,全局变量可以在全局使用,局部变量呢只能在一些地方使用的变量。

在函数里面定义的变量为局部变量,只能试用于函数。

在外面定义的叫全局变量,也可以在函数内部访问。

切记!如果全局变量有一个变量名字跟函数里面的相同,函数内部在调用时,用的是函数内部的变量

澳门新葡萄京官网注册 18澳门新葡萄京官网注册 19

 1 name='zzq'
 2 def say():
 3     name='gmx'
 4     print(name)#调用的是函数内部的name
 5 say()
 6 print(name)#调用的全局变量的name
 7 
 8 #执行结果
 9 #gmx
10 #zzq

View Code

在函数中的局部变量也能转化为全局变量,但是这种方法是不推荐使用的,也是不能使用的。

澳门新葡萄京官网注册 20澳门新葡萄京官网注册 21

 1 name='zzq'
 2 def say():
 3     global name #把name设置为全局变量
 4     name='gmx'
 5     print(name)
 6 say()
 7 print(name)
 8 #执行结果
 9 #gmx
10 #gmx

View Code

匿名函数的递归

函数式编程立志于用函数表达式消除有状态的函数,以及for/while循环,所以,在函数式编程的世界里是不应该用for/while循环的,而要改用递归(递归的性能很差,所以,一般是用尾递归来做优化,也就是把函数的计算的状态当成参数一层一层的往下传递,这样语言的编译器或解释器就不需要用函数栈来帮你保存函数的内部变量的状态了)。

好了,那么,匿名函数的递归该怎么做?

一般来说,递归的代码就是函数自己调用自己,比如我们求阶乘的代码:

function fact(n){
  return n==0 ? 1 :  n * fact(n-1);
};
result = fact(5);

在匿名函数下,这个递归该怎么写呢?对于匿名函数来说,我们可以把匿名函数当成一个参数传给另外一个函数,因为函数的参数有名字,所以就可以调用自己了
如下所示:

function combinator(func) {
  func(func);
}

这个是不是有点作弊的嫌疑?Anyway,我们再往下,把上面这个函数整成箭头函数式的匿名函数的样子。

(func) => (func(func))

现在你似乎就不像作弊了吧。把上面那个求阶乘的函数套进来是这个样子:

首先,先重构一下fact,把fact中自己调用自己的名字去掉:

function fact(func, n) {
  return n==0 ? 1 :  n * func(func, n-1);
}

fact(fact, 5); //输出120

然后,我们再把上面这个版本变成箭头函数的匿名函数版:

var fact = (func, n) => ( n==0 ? 1 :  n * func(func, n-1) )
fact(fact, 5)

这里,我们依然还要用一个fact来保存这个匿名函数,我们继续,我们要让匿名函数声明的时候,就自己调用自己。

也就是说,我们要把

(func, n) => ( n==0 ? 1 :  n * func(func, n-1) )

这个函数当成调用参数,传给下面这个函数:

(func, x) => func(func, x)

最终我们得到下面的代码:

( (func, x) => func(func, x) ) (  //函数体
  (func, n) => ( n==0 ? 1 :  n * func(func, n-1) ), //第一个调用参数
  5 //第二调用参数
);

好像有点绕,anyway, 你看懂了吗?没事,我们继续。

三、递归

  前面已经介绍了函数相关的知识,在函数中,函数可以调用其他的函数,并且函数还可以调用自身,利用这种特性我们可以完成一些特定的

  操作,这种函数调用自身的形式就是递归

  def recursion() :
    return recursion()

  在递归中不能像上面两行一样一直调用自身,这样一会程序就会崩溃,因为它永远的在调用就跟while死循环一样出不去,所以递归也需要进

  判断给它出口

  例子:阶乘

  什么是阶乘,阶乘就是给一个自然数N,然后计算N的阶乘那么 N =
1x2x3x4….N ,这个就是阶乘,我们可以把它到过来看,

  N = N x (n-1) x (n-2) x (n-3) …… 1
一直乘到括号中的值等于1,既然知道了阶乘是什么,那么我们来写一个程序实现它

澳门新葡萄京官网注册 22

1 def factorial(n):
2     for i in range(1,n):
3         n *= i
4     return n
5 c = factorial(4)
6 print(c)

阶乘非递归版本

  剖析:上面的例子首先把n=4传入进去,然后通过 for i in
range(1,4)让i分别等于1,2,3,然后进行
n*=i,我们可以看出这个for循环是循环3次的

  第一次(n = n*i) n = 4*1 ,此时n还是等于4

  第二次(n = 4*i)  n = 4*2 此时n = 8 

  第三次(n = 8*i)  n = 8*3 此时n等于24

  此时for循环了3次所以结束了,通过return把n的结果返回,所以最终结果算出
4的阶乘等于24

  递归版本

  下面看递归版本的阶乘

澳门新葡萄京官网注册 23

1 def factorial(n) :
2     if n == 1:
3         return 1
4     else:
5         return n * factorial(n-1)
6 c = factorial(4)
7 print(c)

阶乘递归版本

  剖析:

  首先c = factorial(4)开始执行函数,然后进行第一次判断 n ==
1,显然第一层n不等于1,然后碰到return n *
factorial(n-1),碰到return本来是要返回的,但是 factorial(n-1)

  有调用了factiorial这个函数,因此进入了第二层

  第二层因为上一层传入的参数是n-1,所以第二层的n是等于3的,然后判断,这一层的n也不等于1,然后又进入第三层

  第三层n等于3,然后判断这一层的n还不等于1,然后又进入第四层

  

  到第四层的时候这时的 n就等于1,所以触发了 return 1
不再调用函数了,所以就开始返回

  返回第三层 return n * factorial(n-1) , 此时factorial(n-1)
就等于第四层return上去的1,所以第三层返回时就等于return n * 1(return 2
* 1),并且第三层n是等于2的

  返回第二层factorial(n-1)就等于第三层return上去的2,并且第二层n是等于3的,return
3 * 2

  返回第一层factorial(n-1)就等于第二层return上去的6,并且第一层n是等于4的,return
4 * 6

  到此为止递归执行完毕,c就等于 4 * 6 c=24

   

   

四,匿名函数

python 使用 lambda
来创建匿名函数,一般一个函数用的次数比较少,为了方便就出现了匿名函数。

  • lambda只是一个表达式,函数体比def简单很多。
  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

匿名函数基本语法:

1 #计算数字的平方
2 calc=lambda n:n*n  #定义一个匿名函数
3 print(calc(5))
4 
5 #当然还能这样用
6 l=map(lambda n:n*n,[1,2,3,4,5]) #map把后面可迭代对象去执行前面的函数
7 for i in l:
8     print(i)

在这里用到了map,那就简单的提一下把。因为要涉及到后面的迭代对象,所以就简单讲一下用法。

map函数的原型是map(function, iterable, …),它的返回结果是一个列表。

参数function传的是一个函数名,可以是python内置的,也可以是自定义的。
参数iterable传的是一个可以迭代的对象,例如列表,元组,字符串这样的。

解释起来有点不好理解,下面一些简单的代码例子,供大家参考吧。

澳门新葡萄京官网注册 24澳门新葡萄京官网注册 25

 1 a=(1,2,3,4,5)
 2 b=[1,2,3,4,5]
 3 c="zhangkang"
 4 
 5 la=map(str,a)
 6 lb=map(str,b)
 7 lc=map(str,c)
 8 
 9 print(la)
10 print(lb)
11 print(lc)
12 
13 输出:
14 ['1', '2', '3', '4', '5']
15 ['1', '2', '3', '4', '5']
16 ['z', 'h', 'a', 'n', 'g', 'k', 'a', 'n', 'g']

View Code

动用高阶函数的递归

但是上面这个递归的匿名函数在自己调用自己,所以,代码中有hard
code的实参。我们想实参去掉,如何去掉呢?我们可以参考前面说过的那个
MakePowerFn 的例子,不过这回是递归版的高阶函数了。

HighOrderFact = function(func){
  return function(n){
    return n==0 ? 1 : n * func(func)(n-1);
  };
};

我们可以看,上面的代码简单说来就是,需要一个函数做参数,然后返回这个函数的递归版本。那么,我们怎么调用呢?

fact = HighOrderFact(HighOrderFact);
fact(5);

连起来写就是:

HighOrderFact ( HighOrderFact ) ( 5 )

但是,这样让用户来调用很不爽,所以,以我们一个函数把 HighOrderFact (
HighOrderFact ) 
给代理一下:

fact = function ( hifunc ) {
  return hifunc ( hifunc );
} (
  //调用参数是一个函数
  function (func) { 
    return function(n){
      return n==0 ? 1 : n * func(func)(n-1);
    };
  }
);

fact(5); //于是我们就可以直接使用了

用箭头函数重构一下,是不是简洁了一些?

fact = (highfunc => highfunc ( highfunc ) ) (
  func => n =>  n==0 ? 1 : n * func(func)(n-1)
);

上面就是我们最终版的阶乘的函数式代码。

四、匿名函数

  匿名函数也叫lambda函数,函数没有具体的名称。语法:function name=
 lambda  args(多个参数用逗号隔开):
Expression(表达式,表达式的结果就是返回值)

  先来看一个最简单例子:

澳门新葡萄京官网注册 26

 1 #普通函数
 2 def func(arg1,arg2):
 3     return arg1-arg2
 4 
 5 #lambda函数
 6 func_2 = lambda arg1,arg2: arg1-arg2
 7 
 8 #传参执行
 9 print(func(5,4))
10 print(func_2(5,4))

匿名函数

  有认识,这个匿名函数和普通函数没有什么区别么,其实匿名函数就像三元运算一样,并且能够用lambda函数有几大优势

  1、在一些不会再别的地方调用的函数,我们可以使用匿名函数,并且这样简化了代码,看起来更加整洁。

  2、lambda函数将会搭配一些内置函数来使用(下面会涉及到)

 

五,嵌套函数

嵌套函数,听到名字难道是一个函数里面还可以放函数,卧槽,居然特么还有这种操作?当然,嵌套函数就是一个函数里面还嵌套另外一个函数,下面一个简单的例子,给大家参考理解吧。

澳门新葡萄京官网注册 27澳门新葡萄京官网注册 28

 1 name='zzq'
 2 def say1():
 3     name='gmx'
 4     def say2():
 5         name='other'
 6         print('最里面函数',name)
 7     say2()
 8     print('外面一层函数',name)
 9 say1()
10 print('最外面函数',name)
11 
12 #执行结果
13 #最里面函数 other
14 #外面一层函数 gmx
15 #最外面函数 zzq

View Code

回顾之前的程序

我们再来看那个查找数组的正常程序:

//正常的版本
function find (x, y) {
  for ( let i = 0; i < x.length; i++ ) {
    if ( x[i] == y ) return i;
  }
  return null;
}

先把for干掉,搞成递归版本:

function find (x, y, i=0) {
  if ( i >= x.length ) return null;
  if ( x[i] == y ) return i;
  return find(x, y, i+1);
}

然后,写出带实参的匿名函数的版本(注:其中的if代码被重构成了
?号表达式):

( (func, x, y, i) => func(func, x, y, i) ) (  //函数体
  (func, x, y, i=0) => (
      i >= x.length ?  null :
         x[i] == y  ?  i : func (func, x, y, i+1)
  ), //第一个调用参数
  arr, //第二调用参数
  2 //第三调用参数
)

最后,引入高阶函数,去除实参:

const find = ( highfunc => highfunc( highfunc ) ) (
   func => (x, y, i = 0) => (
     i >= x.length ?  null :
           x[i] == y  ?  i : func (func) (x, y, i+1)
   )
);

注:函数式编程装逼时一定要用const字符,这表示我写的函数里的状态是
immutable 的,天生骄傲!

再注:我写的这个比原来版的那个简单了很多,原来版本的那个又在函数中套了一套
next,
而且还动用了不定参数,当然,如果你想装逼装到天上的,理论上来说,你可以套N层,呵呵。

现在,你可以体会到,如此逼装的是怎么来的了吧?

五、闭包

  在上面的示例中知道了函数可以调用函数本身,这种形式称之为递归,那么还可以将函数作为参数返回,这种形式就称之为闭包

  闭包最大的好处就是即用即调,闭包对于安装计算,隐藏状态,以及在函数对象和作用域中随意地切换是很有用的!

澳门新葡萄京官网注册 29

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 def func(rate):
 4     count = [0]
 5     def add_func(arg):
 6         count[0] +=1
 7         print("第%s次调用"%count[0])
 8         arg = arg - arg*rate
 9         return arg
10     return add_func
11 
12 closure = func(0.03)
13 print(closure(1000))
14 print(closure(1100))
15 print(closure(1200))

闭包示例

  例子中做了一个减去手续费后返回余额的功能,首先执行了func函数,将利率封装了进去,然后,func函数把它内部的函数进行了进行了返回

  要知道当函数不加括号的时候是不执行的!,所以此时closoure就是 add_func
函数的内存地址,当想要使用这个功能的时候,直接把closoure加括号

  并传入值即可执行。并且可以看到的是在全局作用域中执行,随时可以切换到局部作用域。

 

六,递归

我们刚刚了解了嵌套函数,知道在函数的内部可以继续调用函数,如果在函数的内部调用自己,那么这个函数就是递归。当然递归不能随便乱用,他也有自己的调用特性。

递归特性:

  1. 必须有一个明确的结束条件

  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3.
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

澳门新葡萄京官网注册 30澳门新葡萄京官网注册 31

 1 #输入一个数,输出所有平方小于500的数
 2 def calc(n):
 3     if n>500:
 4         return n
 5     print(n)
 6     return calc(n*n)  #继续调用自己,形成递归
 7 calc(2)
 8 
 9 #执行结果
10 2
11 4
12 16
13 256

View Code

4.阶乘

在递归的运用中,有一个很经典的例子,那就是计算阶乘的结果。用其他方法也可以实现,但是估计比较麻烦,下面是用简单的递归实现阶乘计算。

1 def test(x):
2     if x==1:
3         return x
4     else:
5         return x*test(x-1)

 

其它

你还别说这就是装逼,简单来说,我们可以使用数学的方式来完成对复杂问题的描述,那怕是递归。其实,这并不是新鲜的东西,这是Alonzo
Church 和 Haskell Curry 上世纪30年代提出来的东西,这个就是 Y Combinator
的玩法,关于这个东西,你可以看看下面两篇文章:《The Y Combinator
(Slight
Return)》,《Wikipedia:
Fixed-point
combinator》

六、高阶函数

  函数可以用来当做返回值,可以用调用自己本身,高阶函数就是函数的参数把另一个函数作为参数,这种函数就称之为高阶函数。

澳门新葡萄京官网注册 32

 1 def func_1(num):
 2     return num+1
 3 
 4 def func_2(num):
 5     return num-1
 6 
 7 def func_main(num,func):
 8     #  可以简写成return func(num)
 9     results = func(num)
10     return results
11 
12 results = func_main(10,func_1)
13 print(results)
14 print(func_main(10,func_2))

高阶函数

  编写高阶函数,就是让函数的参数能够接收别的函数。

  

七,高阶函数

变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。通俗的讲就是函数的参数可以是函数,然后里面的参数可以给作为参数的函数当成传入的参数。emmm,好吧,讲得是有点乱。。那就分析分析代码吧

澳门新葡萄京官网注册 33澳门新葡萄京官网注册 34

1 输入ascii的编码号,输出对应的值
2 def say(letter,f):
3     return f(letter)
4 ascii_name=say(65,chr)#f为一个函数
5 print(ascii_name)
6 #执行结果
7 #A

View Code

卧槽,不知不觉学了这么多函数了啊,你就会想这个学了有啥子用嘛,放心,这个为后面厉害的装逼的打基础呢,这章的嵌套函数跟高阶函数为后面的装饰器打基础。万丈高楼平地起,所以还是要把基础掌握好,怎么才能更好的掌握呢,当然方法只有一个,那就是多敲,多记,多思考。

 

七、内置函数

  内置函数就是python中内置的一些方法,内置函数官方介绍请猛戳这里

  澳门新葡萄京官网注册 35

  内置函数使用方法示例,详细介绍请参考

澳门新葡萄京官网注册 36

  1 # !/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 
  4 #返回数字的绝对值。 参数可以是整数或浮点数。 如果参数是复数,则返回其大小。
  5 print(abs(-1.11))
  6 
  7 #传入一个可被循环的元素,如果这个元素中有一个为False则都为假
  8 # 0 空值 False 都为假
  9 print(all([1,2,3]))
 10 
 11 #与all相反,只有一个为真,则为真;
 12 print(any([0,2,False]))
 13 
 14 #这个函数跟repr()函数一样,返回一个可打印的对象字符串方式表示。当遇到非ASCII码时
 15 #就会输出x,u或U等字符来表示。与Python 2版本里的repr()是等效的函数。
 16 print(ascii("dsads"),ascii(66),ascii('b23'))
 17 
 18 #将十进制转换为二进制;
 19 print(bin(10))
 20 
 21 #返回布尔值,即True或False之一,如果参数为false或省略,则返回False; 否则返回True。
 22 print(bool(1))
 23 
 24 #根据传入的参数创建一个新的字节数组
 25 #如果传入字符串必须给出编码
 26 print(bytearray('你好','utf-8'))
 27 #当source参数是一个可迭代对象,那么这个对象中的元素必须符合大于0 小于256
 28 print(bytearray([256,1,2]))
 29 
 30 #返回一个的“bytes”对象,返回bytes类型
 31 bytes('中文','utf-8')
 32 
 33 #检查对象是否可以被调用
 34 def func():
 35     pass
 36 print(callable(func))
 37 
 38 #返回整数所对应的Unicode字符,chr(97)返回字符串'a',而chr(8364)返回字符串'€'。
 39 print(chr(126))
 40 
 41 #是用来指定一个类的方法为类方法,类方法可以不实例化直接调用
 42 class A:
 43     @classmethod
 44     def B(cls,arg1,):
 45         print(arg1)
 46 A.B(1)
 47 A().B(1)
 48 
 49 #将源编译为代码或者AST对象。代码对象能够通过exec语句来执行或者eval()进行求值。
 50 #源可以是正常字符串,字节字符串或AST对象。
 51 expr = "5+5-1"
 52 obj = compile(expr,"","eval")
 53 print(eval(obj))
 54 
 55 #返回值为real + imag * j的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数。
 56 print(complex(1, 2))
 57 print(complex(1))
 58 print(complex("1+2j"))
 59 
 60 
 61 
 62 # 参数是一个对象和一个字符串。 该字符串必须是对象属性之一的名称。
 63 class A:
 64     def a1(self):
 65         print("a1")
 66     def a2(self):
 67         print("a2")
 68 
 69 obj = A
 70 print(dir(obj))
 71 delattr(obj, "a1")
 72 print(dir(obj))
 73 
 74 #dir 返回对象中的方法
 75 strs="aaa"
 76 print(dir(strs))
 77 
 78 #返回两个数值的商和余数
 79 print(divmod(7,3))
 80 
 81 #用于遍历序列中的元素以及它们的下标
 82 print(enumerate([1,2,3]))#返回的是可迭代的对象
 83 for i,j in enumerate(('A','B','C')):
 84     print(i,j)
 85 
 86 #将字符串str当成有效的表达式来求值并返回计算结果。
 87 print(eval("1+2+3"))
 88 print(eval("False or True"))
 89 
 90 #字符串str当成动态语句块执行并返回结果
 91 exec('a=1+2+3')
 92 print(a)
 93 
 94 #使用指定方法(方法,函数),过滤可迭代对象的元素
 95 def add(arg):
 96     return arg > 3
 97 
 98 for i in filter(add,[1,2,3,4,5]):
 99     print(i)
100 
101 #浮点型
102 print(float(11))
103 
104 #格式化显示 更多方法请参考官方说明
105 print('{:,.2f}'.format(111111))
106 
107 #根据传入的参数创建一个新的不可变集合
108 a = frozenset([1,2,3,4,5])
109 print(a)
110 
111 #获取对象的属性值
112 class A():
113     def __init__(self,):
114         self.name = "123"
115 b = A()
116 print(getattr(b,'name'))
117 
118 #返回当前作用域内的全局变量和其值组成的字典
119 print(globals())
120 
121 #检查对象是否含有属性
122 class A():
123     def __init__(self,):
124         self.name = "123"
125 b = A()
126 print(hasattr(b,'name'))
127 
128 #哈希值计算
129 #在当前环境中是唯一的
130 print(hash('Hello'))
131 
132 #help帮助
133 def funcs(args):
134     """
135     Function description
136     :param args: args = list
137     :return:
138     """
139     pass
140 print(help(funcs))
141 
142 #转换16进制
143 print(hex(44))
144 
145 #显示对象的标识符
146 print(id("123"))
147 
148 #input标准输入
149 s = input("user name:")
150 print(s)
151 
152 #int返回整数
153 print(int(1.2))
154 print(int("2"))
155 
156 #判断对象是否是类或者类型元组中任意类元素的实例
157 print(isinstance("1",int))
158 print(isinstance(1.1,(int,float)))
159 
160 
161 #判断类是否是另外一个类或者类型元组中任意类元素的子类
162 print(dir(str))
163 print(issubclass(bytearray,str))
164 print(issubclass(bool,int))
165 
166 #根据传入的参数创建一个新的可迭代对象
167 a = iter('12345')
168 print(next(a))
169 print(next(a))
170 
171 #返回对象的长度len
172 a = [1,2,3,4]
173 
174 #转换列表
175 print(list("abcd"))
176 
177 #返回当前作用域内的局部变量和其值组成的字典
178 def A():
179     print(locals())
180     s = 1
181     print(locals())
182 A()
183 
184 #使用指定方法去作用传入的每个可迭代对象的元素,生成新的可迭代对象
185 def add(x):
186     return x+100
187 lists = [1,2,3,4]
188 for i in map(add,lists):
189     print(i)
190 
191 #max:返回最大值
192 print(max(1,2,3))
193 print(max([1,2,3,4]))
194 
195 #在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存;
196 s = memoryview(b'abcd')
197 print(s[1])
198 
199 #返回最小值
200 print(min(1,2,3))
201 print(min([2,3]))
202 
203 #返回可迭代对象中的下一个元素值
204 a = iter('1234')
205 print(next(a))
206 
207 #创建一个新的object对象(新式类)
208 class B(object):
209     pass
210 
211 #转化成8进制数字符串
212 print(oct(10))
213 
214 #open文件操作
215 file = open('test.txt',encoding="utf-8")
216 
217 #ord:返回Unicode字符对应的整数
218 print(ord("A"))
219 
220 #幂运算
221 
222 print(pow(2,3))
223 
224 #标准输出
225 print()
226 
227 #property:标示属性的装饰器
228 #类中使用具体方法请百度,或者等待后续更新
229 property
230 
231 #range:根据传入的参数创建一个新的range对象
232 range(10)
233 range(1,10)
234 
235 """repr()函数得到的字符串通常可以用来重新获得该对象,repr()的输入对python比较友好。
236 通常情况下obj==eval(repr(obj))这个等式是成立的。"""
237 obj='Python'
238 print(eval(repr(obj)))
239 
240 
241 #翻转序列
242 a = reversed([1,2,3,4,5])
243 print(list(a))
244 
245 #round:对浮点数进行四舍五入求值
246 print(round(1.5))
247 
248 #set 转换成集合
249 print(set([1,2,3]))
250 
251 #setattr:设置对象的属性值
252 class A():
253     def __init__(self,age):
254         self.age = age
255 s = A(11)
256 print(s.age)
257 setattr(s,'age',22)
258 print(s.age)
259 
260 #根据传入的参数创建一个新的切片对象
261 c1 = slice(3)
262 c2 = slice(2,4)
263 c3 = slice(0,5,2)
264 s = [1,2,3,4,5,6]
265 print(s[c1])
266 print(s[c2])
267 print(s[c3])
268 
269 
270 #排序,返回一个新的列表默认按字符ascii码排序
271 a = [4,3,2,1,7]
272 print(sorted(a))
273 
274 #标示方法为静态方法的装饰器
275 class B(object):
276     def __init__(self,age):
277         self.age = age
278 
279     @staticmethod
280     def hello(args):
281         print(args)
282 
283 B.hello("Hello World")
284 
285 #字符串类型
286 print(str(123))
287 
288 #求和
289 print(sum([1,2,3,4]))
290 
291 #根据传入的参数创建一个新的子类和父类关系的代理对象
292 class A(object):
293     def __init__(self):
294         print("我是 A Clase")
295 
296 class B(A):
297     def __init__(self):
298         print("我是 B Class")
299         super().__init__()
300 b = B()
301 
302 #元祖
303 tuple([1,2,3,4])
304 
305 
306 #type 返回对象的类型
307 print(type([1]))
308 print(type("1"))
309 
310 #返回当前作用域内的局部变量和其值组成的字典,或者返回对象的属性列表
311 def func():
312     print(vars())
313     s = 1
314     print(vars())
315 func()
316 
317 #聚合传入的每个迭代器中相同位置的元素,返回一个新的元组类型迭代器
318 list1 = [1,2,3]
319 list2 = ["A","B","C","D"]
320 print(zip(list1,list2))
321 for i in zip(list1,list2):
322     print(i)
323 
324 #__import__:动态导入模块
325 __import__

内置方法

 

 

 

 

 

作者:北京小远
出处:
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Python第三章__函数式编程、递归、闭包 欢迎加入Linux_Python学习群
群号:47861684…

发表评论

电子邮件地址不会被公开。 必填项已用*标注