php抓取网页数据插入数据库(PHP擅长取消调用函数式编程吗?系国内ITOM管理平台)

优采云 发布时间: 2022-01-14 10:05

  php抓取网页数据插入数据库(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 官方博客。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线