PHP的函数式编程

思考并回答以下问题:

  • function a() { return (function(){}); }返回什么类型?
  • 第一等公民是什么意思?
  • 只用“表达式”,不用“语句”是什么意思?
  • 闭包和函数式编程密切相关,可以说函数式编程就是闭包。怎么理解?
  • 只要函数里有相同代码就可以使用闭包,尤其是if else里面有相同的代码。怎么理解?

处理一个数组。

1
2
3
4
5
6
7
8
<?php

declare(strict_types=1);

for ($i=0; $i < 101; $i++)
{
$results[] = $i;
}

最终都是返回Generator生成器。

常见写法

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
/**
* 返回闭包对象
*
*/
function handleArrYield1(array $data)
{
return (function ($data)
{
foreach ($data as $value)
{
yield $value;
}
});
}

$yield1 = handleArrYield1($results);

print_r($yield1); // Closure Object ( [parameter] => Array ( [$data] => ) )

print_r($yield1($results)); // Generator Object ( )

echo '<br/>';

foreach ($yield1($results) as $key => $value)
{
echo $value;
echo "<br/>";
}

常规函数

1
2
3
4
5
6
7
8
9
10
11
12
13
$data = function ($results)
{
foreach ($results as $v)
{
yield $v;
}
};

foreach ($data($results) as $key => $value)
{
echo $value;
echo "<br/>";
}

闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
$data = function () use ($results)
{
foreach ($results as $v)
{
yield $v;
}
};

foreach ($data() as $key => $value)
{
echo $value;
echo "<br/>";
}

抽象写法

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
/**
* 返回生成器对象
*
*/
function handleArrYield2(array $data)
{
return (function ($data)
{
foreach ($data as $value)
{
yield $value;
}
})($data);
}

$yield2 = handleArrYield2($results);

print_r($yield2); // Generator Object ( )

echo "<br/>";

foreach ($yield2 as $key => $value)
{
echo $value;
echo "<br/>";
}

函数式编程

闭包closure是函数式编程。

闭包可以理解成“定义在一个函数内部的函数”。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

实现同样的功能函数式编程所需要的代码比面向对象编程要少很多,代码更加简洁明晰。

函数式编程的特点:函数可以赋给变量,所以,可作为参数传递,可作为返回值返回。PHP返回闭包。

array_walk、array_map等都是函数式编程。

什么是函数式编程

与面向对象编程(Object-oriented programming)和过程式编程(Procedural programming)并列的编程范式。

最主要的特征是,函数是第一等公民。

强调将计算过程分解成可复用的函数,典型例子就是map方法和reduce方法组合而成的MapReduce算法。

只有纯的、没有副作用的函数,才是合格的函数。

PHP中的函数

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$grade = [
[
'id' => 1,
'subject' => 'chinese',
'score' => 82
],
[
'id' => 2,
'subject' => 'math',
'score' => 98
],
[
'id' => 3,
'subject' => 'english',
'score' => 78
],
];

array_filter — 用回调函数过滤数组中的单元

1
2
3
4
// 获取成绩大于80分的学科
$arrayFilter = array_filter($grade, function($item){
return $item['score'] > 80 ;
});

array_map() - 为数组的每个元素应用回调函数

1
2
3
4
5
6
7
8
9
// 不影响原数组,返回一个新数组
$arrayMap = array_map(function($item){
return [
'id' => $item['id'],
'subject' => $item['subject'],
'score' => $item['score'],
'grade' => $item['score'] >= 60 ? '合格' : '不合格',
];
}, $grade);

array_reduce() - 用回调函数迭代地将数组简化为单一的值

1
2
3
4
5
6
7
8
9
// 求分数最高的科目,返回最大科目信息
$maxScore = array_reduce($grade, function($init, $val){
return $init['score'] > $val['score'] ? $init : $val;
}, ['score' => 0]);

// 求平均成绩
$avgScore = array_reduce($grade, function($init, $item){
return $init + $item['score'];
}, 0) / count($grade);

array_walk() - 使用用户自定义函数对数组中的每个元素做回调处理

1
2
3
4
// 修改原数组
array_walk($grade, function(&$item, $index){
$item['grade'] = $item['score'] >= 60 ? '合格' : '不合格';
});

定义

简单说,“函数式编程”是一种“编程范式”(programming paradigm),也就是如何编写程序的方法论。它属于“结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

函数编程式是一种编程风格,它将关注点从执行命令转移到表达式计算。这些表达式是使用函数构成的,结合这些函数可以得到我们要查找的结果。

闭包通常出现在允许将函数处理为第一类值(First-class value)的语言中,这意味着函数可以动态创建并作为参数传递给其他语言。

函数式编程的特点

函数是“第一等公民”

所谓“第一等公民”(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

只用“表达式”,不用“语句”

“表达式”(expression)是一个单纯的运算过程,总是有返回值;“语句”(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。“语句”属于对系统的读写操作,所以就被排斥在外。

当然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。

函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。

副作用

所谓“副作用”,指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。在纯函数式语言中,函数只能读取其参数提供给它的内容,并且它对世界产生影响的唯一方式就是通过它返回的值。

递归和迭代

对于函数式而言,循环体有一个无法避免的副作用,就是它会修改某些对象的状态,通常这些对象又是和其他部分共享的。而且也因为变量值是不可变的,纯函数编程语言也无法实现循环。

所以纯函数编程语言通常不包含像while和for这样的迭代构造器,而是采用的无需修改的递归

0%