php抓取网页数据插入数据库(PHP擅长取消调用函数式编程吗?系国内ITOM管理平台)
优采云 发布时间: 2022-01-14 10:05php抓取网页数据插入数据库(PHP擅长取消调用函数式编程吗?系国内ITOM管理平台)
很多通用编程语言都试图兼容大多数编程范式,PHP就是其中之一。无论您想要成熟的面向对象编程、过程式编程还是函数式编程,PHP 都可以做到。但是我们不禁要问,PHP擅长函数式编程吗?本文由国内ITOM管理平台OneAPM的工程师编写。
我今年冬天开始在 Recurse Center 从事 Clojure 工作,对函数式编程有了更深入的了解,然后又回到 PHP 的客户端工作。但是我还是想用一些高阶函数和概念来研究一下。
我已经在 PHP 中实现了一个模拟 LISP 语言,并且已经看到一些尝试通过使用下划线类库使 PHP 中的一些关键功能方法兼容。但是为了让 Clojure 在编写其他编程语言时保持快速,我特意镜像了 Clojure 的标准库,以便在编写真正的 PHP 代码时可以用 Clojure 的方式思考。虽然在学习的过程中会走一些弯路,但是作者还是愿意给大家展示一下interleave功能的实现方法。
幸运的是,arraysome 和 arrayevery 已经实现了,而且它们非常地道(至少我是这么认为的)。
/**
* 如果给定谓词对所有元素都为真,则返回真。
* 来源:array_every 和 array_some.php
*
*/
function every(callable $callback, array $arr) {
foreach ($arr as $element) {
if (!$callback($element)) {
返回错误;
}
}
返回真;
}
/**
* 如果给定谓词对于至少一个元素为真,则返回真。
* 来源:array_every 和 array_some.php
*
*/
function some(callable $callback, array $arr) {
foreach ($arr as $element) {
if ($callback($element)) {
返回真;
}
}
返回错误;
}
我们可以使用 not-every 函数插入一些易于实现的目标,同时仍然具有相同的签名,只需取消对每个函数的调用即可。
/**
* 如果给定的谓词不是对所有元素都为真,则返回真。
*/
function not_every(callable $callback, array $arr) {
return !every($callable, $arr);
}
如您所见,我已经删除了前缀数组。 PHP的不便之处在于它强调序数函数,经常使用前缀数组对序列进行操作。作者理解这一点,因为这两个函数的作者正在互相模仿。虽然数组已经是 PHP 中事实上的数据结构,但标准数据库以这种方式编写并不常见。
本标准适用于基本的高阶函数,可以使用 arraymap、arrayreduce 和 arrayfilter 结尾来代替 map、recude 和 filter。如果这些还不够,参数不一致。 arrayreduce 和arrayfilter 都将数组作为第一个参数,然后将回调值作为第二个参数,首先回调arraymap。在 Clojure 中,回调函数通常是先运行的,所以让我们重命名这些函数,让这些签名一步到位:
/**
* 对数组中的每一项应用callable,返回新数组。
*/
function map(callable $callback, array $arr) {
return array_map($callback, $arr);
}
/**
* 返回一个新数组,其中收录谓词返回 true 的元素。
*/
函数过滤器(callable $callback, array $arr, $flag=0) {
return array_filter($arr, $callback, $flag);
}
/**
* 使用回调函数迭代地将数组缩减为单个值
*/
function reduce(callable $callback, array $arr, $initial=NULL) {
return array_reduce($arr, $callback, $initial);
}
我们目前没有其他方法,所以当 Clojure 中的 reduce 函数被传递一个初始值作为第二个参数时,它有另一个签名。正因为如此,从现在开始我们将使用initial作为最终值——毕竟它仍然是对原创函数的一个很大的改进。此外,我们将 $flag 保留在过滤器函数中,它决定是传递所有键和值,还是只传递键。
在Clojure中,first和last是两个非常有用的函数,相当于PHP中的arrayshift和arraypop。它们之间的主要区别在于 PHP 中的两个命令都是破坏性的。以arrayshift为例,它返回数组的第一项,同时从原创数组中删除该项(当数组被引用时)。在函数式编程中,目标之一是减轻副作用。所以在后台使用 first 和 last 函数来制作序列的副本,以便原创序列永远不会改变。与此相对应的是rest和but-last函数,我们可以继续使用arrayslice来返回该部分。
/**
* 返回数组中的第一项。
*/
函数优先(数组$arr){
$copy = array_slice($arr, 0, 1, true);
return array_shift($copy);
}
/**
* 返回数组中的最后一项。
*/
函数最后一个(数组 $arr){
$copy = array_slice($arr, 0, NULL, true);
return array_pop($copy);
}
/**
* 返回数组中除第一项以外的所有元素。
*/
函数休息(数组$arr){
return array_slice($arr, 1, NULL, true);
}
/**
* 返回数组中除最后一项之外的所有元素。
*/
function but_last(array $arr) {
return array_slice($arr, 0, -1, true);
}
当然,这些只是低阶函数,可能看起来并不令人兴奋,但它们迟早会派上用场。顺便问一下,有谁知道PHP中这些函数对应的“apply”()?答案可能是否定的。因为它们的名字非常深奥,所以它们不像其他编程语言中具有相同概念但通用名称的命令。让我们继续,将 calluserfunc_array 替换为 apply 函数。
/**
* 申请的别名call_user_func_array。
*/
function apply(callable $callback, array $args) {
return call_user_func_array($callback, $args);
}
这太令人兴奋了!当我们使函数名称惯用并创建低级抽象名称时,我们有一个平台可以帮助我们创建更有趣的名称。让我们使用 apply 来帮助我们创建补语:
函数补码(callable $f) {
返回函数() 使用 ($f) {
$args = func_get_args();
返回 !apply($f, $args);
};
}
这里用到了funcgetargs()函数,当所有的值都通过原函数时,它能够抓取一个数组,其中所有的值都是按照通过的顺序排列的。我们继续并返回通过 use 获取原创函数 $f 的匿名函数(因为所有函数在 PHP 中都有新字段),然后在 $args 中调用 apply 。
太好了,现在我们有了补码函数,它可以更容易地实现与过滤函数相反的删除函数。将返回回调的补码传递给过滤器,当所有数据不符合预设条件时,返回所有数据。
/**
* 返回一个新数组,其中收录谓词返回 false 的元素。
*/
function remove(callable $callback, array $arr, $flag=0) {
返回过滤器(补充($callback),$arr,$flag);
}
换句话说,array_merge 和contact 是等价的。下面是一个 Cons 和 conj 的示例,它们在 Clojure 中是向集合的开头或结尾添加项目的标准方法,
/**
* 别名array_merge to concat.
*/
函数 concat() {
$arrs = func_get_args();
return apply('array_merge', $arrs);
}
/**
* 构造(结构)
* 返回一个新数组,其中 x 是第一个元素,$arr 是其余元素。
*/
函数 cons($x, 数组 $arr) {
return concat(数组($x), $arr);
}
/**
* conj(oin)
* 返回一个添加了 xs 的新 arr。
* @param $arr
* @param & xs add'l args 要添加到 $arr。
*/
函数 conj() {
$args = func_get_args();
$arr = first($args);
return concat($arr, rest($args));
}
例如,现在调用这两个函数会产生相同的结果:
缺点(1, 数组(2, 3, 4));
conj(数组(1), 2, 3, 4);
这些低级工具足以让编写交错变得非常容易。首先,我们使用funcgetargs,而不是在函数签名中使用声明参数,这样我们就可以将大量的数组作为函数参数。然后,我们提出每个序列的第一项来形成一个新的序列,剩下的每个序列都被用作每个新的序列。接下来,检查每个数组是否收录元素,然后使用 concat 函数连接交错数组的结果,依此类推。以可读的实现和与 Clojure 版本几乎没有区别的函数结果结束,结果证明 Clojure 产生惰性序列。
/**
* 返回每个集合中第一项的序列,然后是第二项,依此类推。
*/
函数交错() {
$arrs = func_get_args();
$firsts = map('first', $arrs);
$rests = map('rest', $arrs);
if (every(function($a) { return !empty($a); }, $rests)) {
return concat($firsts, apply('interleave', $rests));
}
返回 $firsts;
}
所以当我们调用变长数组来做函数时:
interleave([1, 2, 3, 4], ["a", "b", "c", "d", "e"], ["w", "x", "y", "z"])
插入所有三个序列减去多余项作为结果序列并以:
数组(
0 => 1,
1 => 'a',
2 => 'w',
3 => 2,
4 => 'b',
5 => 'x',
6 => 3,
7 => 'c',
8 => 'y',
9 => 4,
10 => 'd',
11 => 'z',
)
当然,Clojure 有很多我们在这里没有提到的功能,比如 interleave,它返回惰性序列而不是静态 采集。此外,由于序列像 PHP 中的映射一样加倍,因此像 assoc 这样的模拟方法变得模棱两可。如果你对这些感兴趣并想在下一个项目中使用它们,代码已放在 GitHub 上供你参考。
本文由 OneAPM 工程师编译。 OneAPM 是应用程序性能管理领域的新兴领导者,使业务用户和开发人员可以轻松实现:缓慢的程序代码和实时抓取 SQL 语句。要阅读更多技术文章,请访问 OneAPM 官方博客。