此包已被弃用且不再维护。未建议替代包。

0.3.5 2017-12-01 17:57 UTC

README

Build Status Coverage Status License Latest Stable Version Latest Unstable Version Total Downloads

此库提供编写更功能化的PHP代码的工具。其简洁且一致的API让您以不同的方式提高生产力

目录

  1. 安装
  2. 关于
  3. 用法
    1. 迭代器
    2. 操作
    3. 异常处理
    4. 谓词
    5. Option类型
    6. Result类型
    7. 部分函数应用
    8. 柯里化
  4. 贡献
  5. 开发

关于

编写此库是为了解决几个问题

  • 为PHP中不同的可迭代类型和各个类型可用的不同操作提供一个单一、一致的API:一个API,任何可迭代,总是关联的,访问键。
  • 提供PHP中尚未存在的可迭代处理操作。
  • 使编写闭包变得快速且简单。可以使用谓词工厂生成常见(过滤)条件。
  • 允许开发人员使用可选值类型轻松区分不同的函数输出。这些类型可用于解决类似json_decode()的问题,它在出错时返回NULL,或者在成功解码JSON字符串null时。没有额外的代码,如可选类型,无法区分不同的结果。
  • 尽可能使用原生PHP特性以提高互操作性和性能。命名和参数顺序遵循PHP中的主要约定。这意味着所有迭代器都实现\Iterator,并且内部使用了许多PHP核心函数。
  • 尽可能添加惰性,因此许多操作仅在您实际使用的迭代器项上应用。

安装

在您的项目根目录中运行composer require bartfeenstra/fu

用法

要使用任何代码,您必须首先在文件顶部导入命名空间

<?php
use BartFeenstra\Functional as F;
use BartFeenstra\Functional\Iterable as I;
use BartFeenstra\Functional\Predicate as P;
use function BartFeenstra\Functional\Iterable\iter;
?>

迭代器

可遍历/可迭代的数据结构可以转换为通用迭代器

<?php
// Arrays.
$iterator = iter([3, 1, 4]);

// \Traversable (includes native/Spl iterators).
$iterator = iter(new \ArrayIterator([3, 1, 4]));

// Callables that (return callables that...) return iterators.
$callable = function (){
  return function () {
    return iter([]);
  };
};
$iterator = iter($callable);

// Existing universal iterators are passed through.
$iterator = iter([]);
assert($iterator === iter($iterator));

// Objects can expose universal iterators as well.
$toIterator = new class() implements I\ToIterator {
  public function iter(): I\Iterator {
    return iter([]);
  }
};
$iterator = iter($toIterator);
?>

操作

以下操作与迭代器值一起工作,在用户提供的回调的情况下甚至包括键

each

为每个值执行代码。

<?php
$carrier = [];
$list = [3, 1, 4];
iter($list)->each(function (int $i) use (&$carrier) {
  $carrier[] = $i;
});
assert($list === $carrier);
?>

filter

过滤掉不匹配的值。

<?php
$result = iter([3, 1, 4])->filter(P\gt(2));
assert([0 => 3, 2 => 4] === $result->toArray());
?>

find

尝试找到单个匹配的值。

<?php
$found = iter([3, 1, 4, 1, 5, 9])->find(P\gt(4));
assert(new I\SomeItem(5, 4) == $found);
?>

map

逐个转换所有值。

<?php
$original = [3, 1, 4];
$expected = [9, 3, 12];
$result = iter($original)->map(function (int $i): int {
  return 3 * $i;
});
assert($expected === $result->toArray());
?>

mapKeys

逐个转换所有键。

<?php
$original = [
    3 => 'c',
    1 => 'a',
    4 => 'd',
];
$expected = [
    9 => 'c',
    3 => 'a',
    12 => 'd',
];
$result = iter($original)->mapKeys(function (string $value, int $key): int {
  return 3 * $key;
});
assert($expected === $result->toArray());
?>

reduce

将所有值合并成一个。

<?php
$list = [3, 1, 4];
$sum = iter($list)->reduce(function (int $sum, int $item): int {
  return $sum + $item;
});
assert(new F\SomeValue(8) == $sum);
?>

在处理完所有项目之前终止合并,抛出带有最终载体的TerminateReduction

fold

将所有值合并成一个,带有默认起始值。

<?php
$start = 2;
$list = [3, 1, 4];
$total = iter($list)->fold(function (int $total, int $item): int {
  return $total + $item;
}, $start);
assert(10 === $total);
?>

在处理完所有项目之前终止合并,抛出带有最终载体的TerminateFold

take

n个值。

<?php
$start = 2;
$list = [3, 1, 4, 1, 5, 9];
$result = iter($list)->take(4);
assert([3, 1, 4, 1] === $result->toArray());
?>

takeWhile

尽可能从开始取连续匹配的值。

<?php
$start = 2;
$list = [3, 1, 4, 1, 5, 9];
$result = iter($list)->takeWhile(P\le(3));
assert([3, 1] === $result->toArray());
?>

slice

将值切割成更小的集合。

<?php
$start = 2;
$list = [3, 1, 4, 1, 5, 9];
$result = iter($list)->slice(2, 3);
assert([2 => 4, 3 => 1, 4 => 5] === $result->toArray());
?>

min

获取最小值。

<?php
$list = [3, 1, 4, 1, 5, 9];
$min = iter($list)->min();
assert(new F\SomeValue(1) == $min);
?>

max

获取最大值。

<?php
$list = [3, 1, 4, 1, 5, 9];
$max = iter($list)->max();
assert(new F\SomeValue(9) == $max);
?>

sum

求所有值的和。

<?php
$list = [3, 1, 4, 1, 5, 9];
$sum = iter($list)->sum();
assert(new F\SomeValue(23) == $sum);
?>

forever

无限重复值的集合。

<?php
$list = [3, 1, 4];
$iterator = iter($list)->forever();
$expected = [3, 1, 4, 3, 1, 4, 3];
assert($expected === iterator_to_array($iterator->take(7), false));
?>

zip

将两个或更多可迭代的值合并成元组

<?php
$one = [3, 1, 4];
$two = [1, 5, 9];
$three = [2, 9, 2];
$zip = iter($one)->zip($two, $three);
$expected = [[3, 1, 2], [1, 5, 9], [4, 9, 2]];
assert($expected === iterator_to_array($zip));
?>

list

将所有键转换为整数,从0开始。

<?php
$array = [
    'a' => 'A',
    'b' => 'B',
    'c' => 'C',
];
$indexed = iter($array)->list();
$expected = ['A', 'B', 'C'];
assert($expected === iterator_to_array($indexed));
?>

listKeys

使用键作为值,并从0开始索引。

<?php
$array = [
    'a' => 'A',
    'b' => 'B',
    'c' => 'C',
];
$keys = iter($array)->listKeys();
$expected = ['a', 'b', 'c'];
assert($expected === iterator_to_array($keys));
?>

flip

交换键和值,类似于array_flip()

<?php
$array = [
    'a' => 3,
    'b' => 1,
    'c' => 4,
];
$flipped = iter($array)->flip();
$expected = [
    3 => 'a',
    1 => 'b',
    4 => 'c',
];
assert($expected === $flipped->toArray());
?>

reverse

反转值的顺序。

<?php
$array = [3, 1, 4];
$reverse = iter($array)->reverse();
assert([4, 1, 3] === $reverse->toArray());
?>

first

获取第一个值。

<?php
$array = [3, 1, 4, 1, 5, 9];
assert(new I\SomeItem(3, 0) == iter($array)->first());
?>

last

获取最后一个值。

<?php
$array = [3, 1, 4, 1, 5, 9];
assert(new I\SomeItem(9, 5) == iter($array)->last());
?>

empty

检查是否存在值。

<?php
assert(TRUE === iter([])->empty());
assert(FALSE === iter([3, 1, 4])->empty());

?>

sort

按值对项进行排序。

<?php
$array = [
    3 => 'c',
    1 => 'a',
    4 => 'd',
];
// ::sort() also takes an optional custom comparison callable.
$sort = iter($array)->sort();
$expected = [
    1 => 'a',
    3 => 'c',
    4 => 'd',
];
assert($expected === iterator_to_array($sort));
?>

sortKeys

按键对项进行排序。

<?php
$array = [
    'c' => 3,
    'a' => 1,
    'd' => 4,
];
// ::sortKeys() also takes an optional custom comparison callable.
$sort = iter($array)->sortKeys();
$expected = [
    'a' => 1,
    'c' => 3,
    'd' => 4,
];
assert($expected === iterator_to_array($sort));
?>

chain

将其他可迭代项链接到现有迭代器,并重新索引值。

<?php
$arrayOne = [3, 1, 4];
$arrayTwo = [1, 5, 9];
$arrayThree = [2, 6, 5];
$iterator = iter($arrayOne)->chain($arrayTwo, $arrayThree);
$expected = [3, 1, 4, 1, 5, 9, 2, 6, 5];
assert($expected === $iterator->toArray());
?>

flatten

将迭代器中包含的可迭代对象展平到一个新的迭代器中。

<?php
$array = [
    [3, 1, 4],
    [1, 5, 9],
    [2, 6, 5],
];
$iterator = iter($array)->flatten();
$expected = [3, 1, 4, 1, 5, 9, 2, 6, 5];
assert($expected === $iterator->toArray());
?>

unique

移除所有重复的值。

<?php
$objectOne = new \stdClass();
$objectTwo = new \stdClass();
$array = [0, false, false, null, [], [], '0', $objectOne, $objectOne, $objectTwo];
$iterator = iter($array)->unique();
$expected = [
    0 => 0,
    1 => false,
    3 => null,
    4 => [],
    6 => '0',
    7 => $objectOne,
    9 => $objectTwo,
];
assert($expected === $iterator->toArray());
?>

异常处理

复杂的try/catch块可以被替换并轻松转换为Result

<?php
// Try executing a callable, catch all exceptions, and output a Result.
$result = F\try_except(function () {/** ... */});

// Try executing a callable, catch all Foo, Bar, Baz, and Qux exceptions, and output a Result.
$result = F\try_except(function () {/** ... */}, Foo::class, Bar::class, Baz::class, Qux::class);

// Try executing a callable at most twice, catch all exceptions, and output a Result.
$result = F\retry_except(function () {/** ... */});

// Try executing a callable at most 5 times, catch all Foo, Bar, Baz, and Qux exceptions, and output a Result.
$result = F\retry_except(function () {/** ... */}, 5, Foo::class, Bar::class, Baz::class, Qux::class);
?>

谓词

谓词可以与filter()find()一起使用。它们可以是任何接受单个参数并返回布尔值的可调用函数,但我们添加了一些用于常见条件的快捷方式。这些函数接受配置参数,并返回谓词。

<?php
// All values strictly identical to TRUE.
$predicate = P\true();

// All values strictly identical to FALSE.
$predicate = P\false();

// All values that evaluate to TRUE.
$predicate = P\truthy();

// All values that evaluate to FALSE.
$predicate = P\falsy();

// All values strictly identical to 0.
$predicate = P\id(0);

// All values equal to "Apples and oranges".
$predicate = P\eq('Apples and oranges');

// All values greater than 9.
$predicate = P\gt(9);

// All values greater than or equal to 99.
$predicate = P\ge(99);

// All values lesser than 15.
$predicate = P\lt(15);

// All values lesser than or equal to 666.
$predicate = P\le(666);

// All values that are instances of Foo, Bar, Baz, or Qux.
$predicate = P\instance_of(Foo::class, Bar::class, Baz::class, Qux::class);

// One or more values are lesser than 0 OR greater than 9.
$predicate = P\any(P\lt(0), P\gt(9));

// All values are greater than 0 AND lesser than 9.
$predicate = P\all(P\gt(0), P\lt(9));

// All values different from "Apples and oranges".
$predicate = P\not(P\eq('Apples and oranges'));
?>

Option类型

在PHP中,NULL表示值的缺失,但它本身也被用作一个值。在这种情况下,Option类型有助于区分作为值的NULL和完全没有值的情况。

<?php
use BartFeenstra\Functional\Option;
use BartFeenstra\Functional\Some;
use BartFeenstra\Functional\SomeValue;
use BartFeenstra\Functional\None;
function get_option(): Option {
    if (true) {
        return new SomeValue(666);
    }
    return new None();
}
function handle_option(Option $value) {
    if ($value instanceof Some) {
        print sprintf('The value is %s.', $value());
    }
    // $value is an instance of None.
    else {
        print 'No value could be retrieved.';
    }
}
handle_option(get_option());
?>

Result类型

Result类型可用于补充或替换异常。因此,它由如try_except()这样的函数返回。它表示成功和值,或错误。

<?php
use BartFeenstra\Functional\Ok;
use BartFeenstra\Functional\OkValue;
use BartFeenstra\Functional\Result;
use BartFeenstra\Functional\ThrowableError;
function get_result(): Result {
    try {
        // Do some things that may throw a ResultComputationException.
        return new OkValue(666);
    }
    catch (\ResultComputationException $e) {
        return new ThrowableError($e);
    }
}
function handle_result(Result $result) {
    if ($result instanceof Ok) {
        print sprintf('The value is %s.', $result());
    }
    // $value is an instance of Error.
    else {
        print sprintf('An error occurred: %s.', $result);
    }
}
handle_result(get_result());
?>

部分函数应用

部分函数应用是基于现有函数创建一个新函数(带有零个或多个参数),通过在调用之前固定原始函数的一个或多个参数。实际上,这允许你复制一个函数,并在调用之前填充一些参数。你可以使用它来快速将现有函数转换为匿名函数,这些函数可以用作回调。在PHP中,这可以通过任何类型的可调用(函数、方法、闭包等)实现。

<?php
$originalFunction = function (string $a, string $b, string $c, string $d): string {
    return $a . $b . $c . $d;
};

// Fix the two first/left-handed arguments.
$newFunction = F\apply_l($originalFunction, 'A', 'B');
assert('ABCD' === $newFunction('C', 'D'));

// Fix the two last/right-handed arguments.
$newFunction = F\apply_r($originalFunction, 'C', 'D');
assert('ABCD' === $newFunction('A', 'B'));

// Fix two arguments by index/in the middle.
$newFunction = F\apply_i($originalFunction, 1, 'B', 'C');
assert('ABCD' === $newFunction('A', 'D'));
?>

柯里化

柯里化将具有n个参数的单个函数转换为具有单个参数的n个函数。实际上,这允许你复制一个函数,并在调用之前逐个填充一些参数。你可以使用它来快速将现有函数转换为匿名函数,这些函数可以用作回调。在PHP中,这可以通过任何类型的可调用(函数、方法、闭包等)实现。

<?php
$originalFunction = function (string $a, string $b, string $c, string $d = 'D'): string {
    return $a . $b . $c . $d;
};

assert('ABCD' === F\curry($originalFunction)('A')('B')('C'));
?>

贡献

欢迎您的参与。请在问题中留下反馈,或通过拉取请求提交代码改进。

互联网和这个项目是一个属于所有人的地方。我们将保持其友好和高效,如我们的行为准则中所述,其中还包含了项目维护者的联系方式,以便您在需要的情况下报告情况,代表自己或他人。

开发

构建代码

运行./bin/build

测试代码

运行./bin/test

修复代码

运行./bin/fix来自动修复可修复的部分。

代码风格

所有PHP代码遵循PSR-2