- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章进一步理解Python中的函数编程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
我们最好从最难的问题开始:“到底什么是函数编程 (FP)?”一个答案可能会说 FP 就是您在使用例如 Lisp、Scheme、Haskell、ML、OCAML、Clean、Mercury、Erlang(或其它一些)语言进行编程时所做的。这是一个稳妥的答案,但不能很确切地阐明问题。不幸的是,即使是函数程序员他们自己也很难对 FP 究竟是什么有个一致的认识。“盲人摸象”的故事用来形容这一情况似乎很合适。还可以放心地将 FP 与“命令编程”(使用例如 C、Pascal、C++、Java、Perl、Awk、TCL 以及其它大多数语言所执行的操作,至少是在很大程度上)进行对比.
从个人角度来说,我会将函数编程粗略地描绘为至少具有以下几个特征。称得上函数性的语言使这些事情变得简单,而使其它事情变得困难或不可能:
函数编程的提倡者认为所有这些特征都导致更快速的开发更短以及错误更少的代码。而且,计算机科学、逻辑和数学领域的高级理论学家发现证明函数语言和程序的正式性能比命令语言和程序容易得多。 固有的 Python 函数能力 。
自从 Python 1.0 以来,Python 具有上面列出的大多数 FP 特征。但对于大多数 Python 特性,它们以一种非常混合的语言呈现。很大程度上是因为 Python 的 OOP 特性,您可以使用希望使用的部分而忽略其余部分(直到在稍后需要它为止)。使用 Python 2.0, 列表内涵添加了一些 非常棒的“句法上的粉饰”。虽然列表内涵没有添加什么新的能力,但它们使许多旧的能力看起来好了 许多.
Python 中 FP 的基本元素是函数 map() 、 reduce() 和 filter() ,以及运算符 lambda 。在 Python 1.x 中, apply() 函数对于将一个函数的列表返回值直接应用于另一个函数也很方便。Python 2.0 为这一目的提供了改进的语法。可能让人吃惊,但很少的这几个函数(以及基本运算符)几乎足以编写任何 Python程序;特别是,所有的流控制语句( if 、 elif 、 else 、 assert 、 try 、 except 、 finally 、 for 、 break 、 continue 、 while 、 def )可以只使用 FP 函数和运算符以函数风格处理。虽然实际上消除程序中的所有流控制命令可能只对加入“混乱的 Python”竞争(与看上去非常象 Lisp 的代码)有用,但是理解 FP 是如何使用函数和递归来表示流控制是值得的.
消除流控制语句 。
在我们执行消除联系时要考虑的第一件事是 Python “短路”了布尔表达式的求值这一事实。这样就提供了表达式版本的 if / elif / else 块(假设每块都调用一个函数,通常总有可能这样安排)。下面是具体方法: 清单 1. Python 中的“短路”条件调用 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
# Normal statement-based flow control
if
<cond1>: func1()
elif
<cond2>: func2()
else
: func3()
# Equivalent "short circuit" expression
(<cond1>
and
func1())
or
(<cond2>
and
func2())
or
(func3())
# Example "short circuit" expression
>>> x
=
3
>>>
defpr
(s):
return
s
>>> (x
=
=
1
and
pr(
'one'
))
or
(x
=
=
2
and
pr(
'two'
))
or
(pr(
'other'
))
'other'
>>> x
=
2
>>> (x
=
=
1
and
pr(
'one'
))
or
(x
=
=
2
and
pr(
'two'
))
or
(pr(
'other'
))
'two'
|
表达式版本的条件性调用似乎不过是个位置诀窍;不过,如果我们注意到 lambda 运算符必须返回表达式时,就更有趣了。因为 -- 如前所示 -- 表达式可以通过短路来包含条件块,所以 lambda 表达式在表达条件返回值中非常普通。在我们的示例上构建: 清单 2. Python 中 Lambda 短路 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
>>> pr
=
lambda
s:s
>>> namenum
=
lambda
x: (x
=
=
1
and
pr(
"one"
)) \
....
or
(x
=
=
2
and
pr(
"two"
)) \
....
or
(pr(
"other"
))
>>> namenum(
1
)
'one'
>>> namenum(
2
)
'two'
>>> namenum(
3
)
'other'
|
函数作为第一类对象 。
上面的示例已经显示出函数在 Python 中所处的第一类的地位,但以很微妙的方式。在使用 lambda 操作创建 函数对象 时,我们有一些完全常规的事物。正是因为这样,我们可以将对象与名称 "pr" 和 "namenum" 绑定,使用的方法和将数字 23 或字符串 "spam" 与这些名称绑定的方法完全相同。但正如我们可以使用数字 23 而无需将它与任何名称绑定一样(换句话说,象函数自变量一样),我们可以使用用 lambda 创建的函数对象而不用将它与任何名称绑定。一个函数只是我们在 Python 中对其执行某些操作的另一个值.
我们对第一类对象所执行的主要操作是将它们传递给 FP 内置函数 map() 、 reduce() 和 filter() 。这些函数中的每一个都接受函数对象作为其第一个自变量.
map() 对指定列表中每个对应的项执行传递的函数,并返回结果列表。 reduce() 对每个后续项执行传递的函数,返回的是最终结果的内部累加;例如 reduce(lambda n,m:n*m, range(1,10)) 意味着“10 的阶乘”(换句话说,用每一项乘上前一次相乘的乘积)。 filter() 使用传递的函数对列表中的每一项“求值”,然后返回经过甄别的,通过了传递函数测试的项的列表.
我们还经常将函数对象传递给自己的定制函数,但它们通常等同于上述内置函数的组合.
通过将这三种 FP 内置函数进行组合,可以执行惊人的一系列“流”操作(都不使用语句,而只使用表达式).
Python 中的函数循环 。
替换循环与替换条件块一样简单。 for 可以直接转换成 map() 。对于我们的条件执行,我们需要将语句块简化成单一函数调用(我们正逐步接近通常的做法): 清单 3. Python 中的函数 'for' 循环 。
1
2
3
4
5
6
7
|
for
e
in
lst: func(e)
# statement-based loop
map
(func,lst)
# map()-based loop
|
另外,对于连续程序流的函数方法有类似的技术。即,命令编程通常包含接近于“做这样,然后做那样,然后做其它事。”这样的语句。 map() 让我们正好做到这一点: 清单 4. Python 中的函数连续操作 。
1
2
3
4
5
6
7
|
# let's create an execution utility function
do_it
=
lambda
f: f()
# let f1, f2, f3 (etc) be functions that perform actions
map
(do_it, [f1,f2,f3])
# map()-based action sequence
|
通常,我们的整个 main 程序可以是 map() 表达式和一系列完成程序所需要执行的函数。第一类函数的另一个方便的特性就是可以将它们放在一个列表中.
while 的转换稍微复杂了一些,但仍然可以直接进行: 清单 5. Python 中的函数 'while' 循环 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# statement-based while loop
while
<cond>:
<pre
-
suite>
if
<break_condition>:
break
else
:
<suite>
# FP-style recursive while loopp
defwhile_block
():
<pre
-
suite>
if
<break_condition>:
return
1
else
:
<suite>
return
0
while_FP
=
lambda
: (<cond>
and
while_block())
or
while_FP()
while_FP()
|
while 的转换仍需要 while_block() 函数,它本身包含语句而不仅仅是表达式。但我们需要对该函数做进一步的消除(例如对模板中的 if/else 进行短路)。另外,因为循环主体(按设计)无法更改任何变量值,所以 <cond> 很难用在一般的测试中,例如 while myvar==7 (那么,将在 while_block() 中修改全部内容)。添加更有用条件的一个方法是让 while_block() 返回一个更有趣的值,然后将这个返回值与终止条件进行比较。有必要看一下这些消除语句的具体示例: 清单 6. Python 中的函数 'echo' 循环 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# imperative version of "echo()"
defecho_IMP
():
while
1
:
x
=
raw_input
(
"IMP -- "
)
if
x
=
=
'quit'
:
break
else
print
x
echo_IMP()
# utility function for "identity with side-effect"
defmonadic_print
(x):
print
x
return
x
# FP version of "echo()"
echo_FP
=
lambda
: monadic_print(
raw_input
(
"FP -- "
))
=
=
'quit'
or
echo_FP()
echo_FP()
|
我们所完成的是设法将涉及 I/O、循环和条件语句的小程序表示成一个带有递归的纯表达式(实际上,如果需要,可以表示成能传递到任何其它地方的函数对象)。我们 的确 仍然利用了实用程序函数 monadic_print() ,但这个函数是完全一般性的,可以在我们以后创建的每个函数程序表达式中重用(它是一次性成本)。请注意,任何包含 monadic_print(x) 的表达式所 求值 的结果都是相同的,就象它只包含 x 一样。FP(特别是 Haskell)对于“不执行任何操作,在进程中有副作用”的函数具有“单一体”意思.
消除副作用 。
在除去完美的、有意义的语句不用而代之以晦涩的、嵌套的表达式的工作后,一个很自然的问题是:“为什么?!”我对 FP 的所有描述都是使用 Python 做到的。但最重要的特性 -- 可能也是具体情况中最有用的特性 -- 是它消除了副作用(或者至少对一些特殊领域,例如单一体,有一些牵制作用)。绝大部分程序错误 -- 和促使程序员求助于调试来解决的问题 -- 之所以会发生,是因为在程序执行过程期间,变量包含了意外的值。函数程序只不过根本就不为变量分配值,从而避免了这一特殊问题.
让我们看一段相当普通的命令代码。它的目的是打印出乘积大于 25 的几对数字的列表。组成各对的数字本身是从另外两个列表中挑选出的。这种操作与程序员在他们程序段中实际执行的操作差不多。实现这一目的的命令方法如下: 清单 7. “打印大乘积”的命令 Python 代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# Nested loop procedural style for finding big products
xs
=
(
1
,
2
,
3
,
4
)
ys
=
(
10
,
15
,
3
,
22
)
bigmuls
=
[]
# ...more stuff...
for
x
in
xs:
for
y
in
ys:
# ...more stuff...
if
x
*
y >
25
:
bigmuls.append((x,y))
# ...more stuff...
# ...more stuff...
print
bigmuls
|
这个项目太小,以至于没有什么可能出错。但我们的目的可能嵌在要同时实现许多其它目的的代码中。用 "more stuff" 注释的那些部分是副作用可能导致错误发生的地方。在这些地方中的任何一处,变量 xs 、 ys 、 bigmuls 、 x 、 y 有可能获得假设节略代码中的意外值。而且,在执行完这一段代码后,所有变量都可能具有稍后代码可能需要也可能不需要的一些值。很明显,可以使用函数/实例形式的封装和有关作用域的考虑来防止出现这种类型的错误。而且,您总是可以在执行完变量后 del 它们。但在实际中,这些指出类型的错误非常普遍.
目标的函数方法完全消除了这些副作用错误。以下是可能的一段代码: 清单 8. “打印大乘积”的函数 Python 代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
bigmuls
=
lambda
xs,ys:
filter
(
lambda
(x,y):x
*
y >
25
, combine(xs,ys))
combine
=
lambda
xs,ys:
map
(
None
, xs
*
len
(ys), dupelms(ys,
len
(xs)))
dupelms
=
lambda
lst,n:
reduce
(
lambda
s,t:s
+
t,
map
(
lambda
l,n
=
n: [l]
*
n, lst))
print
bigmuls((
1
,
2
,
3
,
4
),(
10
,
15
,
3
,
22
))
|
在示例中,我们将匿名 ( lambda ) 函数对象与名称进行绑定,但这不是一定必要的。我们可以只嵌套定义。这样做是出于可读性目的;但也是因为 combine() 是一种随处可得的很好实用程序函数(从两个输入列表中产生所有元素对的列表)。随后的 dupelms() 主要只是帮助 combine() 发挥作用的一种方法。即使这一函数示例比命令示例更冗长,但一旦考虑到实用程序函数可以重用,那么 bigmuls() 中的新代码本身可能比命令版本中的代码数量还要少一些.
这种函数示例真正的优势在于绝对不会有变量更改其中的任何值。稍后的代码中没有 可能的不曾预料到的副作用(较早的代码中也不会有)。很明显,它本身没有副作用并不能保证代码 正确,但即使这样,这也是个优点。不过请注意,Python(与许多函数语言不同) 不能 防止名称 bigmuls 、 combine 和 dupelms 的重新绑定。如果 combine() 在程序的稍后部分中开始有其它意义,则所有努力都前功尽弃。您可以逐步建立一个 Singleton 类来包含这种类型的不可变绑定(例如 s.bigmuls 等);但本专栏并不涉及这一内容.
特别值得注意的一个问题是我们的特定目的是对 Python 2 中的新特性进行定制。最好的(也是函数的)技术既不是上面提供的命令示例,也不是函数实例,而是: 清单 9. "bigmuls" 的列表内涵 Python 代码 。
1
2
3
4
5
6
7
8
9
10
11
12
|
print
[(x,y)
for
x
in
(
1
,
2
,
3
,
4
)
for
y
in
(
10
,
15
,
3
,
22
)
if
x
*
y >
25
]
|
结束语 。
我已介绍了使用函数等价物替换每个 Python 流控制构造所使用的方法(在过程中消除了副作用)。对特定程序进行有效转换将带来一些额外的考虑,但我们已经知道内置函数是常规而完整的。在稍后的专栏中,我们将考虑一些更高级的函数编程技术;希望能够探索函数风格的更多利弊.
最后此篇关于进一步理解Python中的函数编程的文章就讲到这里了,如果你想了解更多关于进一步理解Python中的函数编程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试打印 timeval 类型的值。实际上我可以打印它,但我收到以下警告: 该行有多个标记 格式“%ld”需要“long int”类型,但参数 2 的类型为“struct timeval” 程序
我正在编写自己的 unix 终端,但在执行命令时遇到问题: 首先,我获取用户输入并将其存储到缓冲区中,然后我将单词分开并将它们存储到我的 argv[] 数组中。IE命令是“firefox”以启动存储在
我是 CUDA 的新手。我有一个关于一个简单程序的问题,希望有人能注意到我的错误。 __global__ void ADD(float* A, float* B, float* C) { con
我有一个关于 C 语言 CGI 编程的一般性问题。 我使用嵌入式 Web 服务器来处理 Web 界面。为此,我在服务器中存储了一个 HTML 文件。在此 HTML 文件中包含 JavaScript 和
**摘要:**在代码的世界中,是存在很多艺术般的写法,这可能也是部分程序员追求编程这项事业的内在动力。 本文分享自华为云社区《【云驻共创】用4种代码中的艺术试图唤回你对编程的兴趣》,作者: break
我有一个函数,它的任务是在父对象中创建一个变量。我想要的是让函数在调用它的级别创建变量。 createVariable testFunc() [1] "test" > testFunc2() [1]
以下代码用于将多个连续的空格替换为1个空格。虽然我设法做到了,但我对花括号的使用感到困惑。 这个实际上运行良好: #include #include int main() { int ch, la
我正在尝试将文件写入磁盘,然后自动重新编译。不幸的是,某事似乎不起作用,我收到一条我还不明白的错误消息(我是 C 初学者 :-)。如果我手动编译生成的 hello.c,一切正常吗?! #include
如何将指针值传递给结构数组; 例如,在 txt 上我有这个: John Doe;xxxx@hotmail.com;214425532; 我的代码: typedef struct Person{
我尝试编写一些代码来检索 objectID,结果是 2B-06-01-04-01-82-31-01-03-01-01 . 这个值不正确吗? // Send a SysObjectId SNMP req
您好,提前感谢您的帮助, (请注意评论部分以获得更多见解:即,以下示例中的成本列已添加到此问题中;西蒙提供了一个很好的答案,但成本列本身并未出现在他的数据响应中,尽管他提供的功能与成本列一起使用) 我
我想知道是否有人能够提出一些解决非线性优化问题的软件包的方法,而非线性优化问题可以为优化解决方案提供整数变量?问题是使具有相等约束的函数最小化,该函数受某些上下边界约束的约束。 我已经在R中使用了'n
我是 R 编程的初学者,正在尝试向具有 50 列的矩阵添加一个额外的列。这个新列将是该行中前 10 个值的平均值。 randomMatrix <- generateMatrix(1,5000,100,
我在《K&R II C 编程 ANSI C》一书中读到,“>>”和“0; nwords--) sum += *buf++; sum = (sum >>
当下拉列表的选择发生变化时,我想: 1) 通过 div 在整个网站上显示一些 GUI 阻止覆盖 2)然后处理一些代码 3) 然后隐藏叠加层。 问题是,当我在事件监听器函数中编写此逻辑时,将执行 onC
我正在使用 Clojure 和 RESTEasy 设计 JAX-RS REST 服务器. 据我了解,用 Lisp 系列语言编写的应用程序比用“传统”命令式语言编写的应用程序更多地构建为“特定于领域的语
我目前正在研究一种替代出勤监控系统作为一项举措。目前,我设计的用户表单如下所示: Time Stamp Userform 它的工作原理如下: 员工将选择他/她将使用的时间戳类型:开始时间、超时、第一次
我是一名学生,试图自学编程,从在线资源和像您这样的人那里获得帮助。我在网上找到了一个练习来创建一个小程序来执行此操作: 编写一个程序,读取数字 a 和 b(长整型)并列出 a 和 b 之间有多少个数字
我正在尝试编写一个 shell 程序,给定一个参数,打印程序的名称和参数中的每个奇数词(即,不是偶数词)。但是,我没有得到预期的结果。在跟踪我的程序时,我注意到,尽管奇数词(例如,第 5 个词,5 %
只是想知道是否有任何 Java API 可以让您控制台式机/笔记本电脑外壳上的 LED? 或者,如果不可能,是否有可能? 最佳答案 如果你说的是前面的 LED 指示电源状态和 HDD 繁忙状态,恐怕没
我是一名优秀的程序员,十分优秀!