PHP, итерируемый в массив или проходимый
Я очень рад, что PHP 7.1 представил повторяющийся псевдотип.
Теперь, хотя это здорово, когда просто перебираешь параметр этого типа, мне непонятно, что делать, когда вам нужно передать его в функции PHP, которые принимают только array
или просто Traversable
. Например, если вы хотите выполнить array_diff, и ваш iterable
равен Traversable
, вы получите array
. И наоборот, если вы вызовете функцию, которая принимает итератор, вы получите ошибку, если iterable
является array
.
Есть ли что-то вроде iterable_to_array
(НЕ: iterator_to_array
) и iterable_to_traversable
?
Я ищу решение, которое позволяет избежать условностей в моих функциях, просто чтобы устранить эту разницу, и это не зависит от того, определяю ли я свои собственные глобальные функции.
Использование PHP 7.1
5 answers
Не уверен, что это то, что вы ищете, но это самый короткий способ сделать это.
$array = [];
array_push ($array, ...$iterable);
Я не очень понимаю, почему это работает. Просто мне показался интересным ваш вопрос, и я начинаю возиться с PHP
Полный пример:
<?php
function some_array(): iterable {
return [1, 2, 3];
}
function some_generator(): iterable {
yield 1;
yield 2;
yield 3;
}
function foo(iterable $iterable) {
$array = [];
array_push ($array, ...$iterable);
var_dump($array);
}
foo(some_array());
foo(some_generator());
Было бы неплохо, если бы он работал с функцией array()
, но поскольку это языковая конструкция, она немного особенная. Он также не сохраняет ключи в ассоциативных массивах.
Есть ли что-то вроде iterable_to_array и iterable_to_traversable
Просто добавьте их куда-нибудь в свой проект, они не занимают много места и предоставляют вам точные API, которые вы просили.
function iterable_to_array(iterable $it): array {
if (is_array($it)) return $it;
$ret = [];
array_push($ret, ...$it);
return $ret;
}
function iterable_to_traversable(iterable $it): Traversable {
yield from $it;
}
Термины легко смешивать
- Проходимый
- Итератор (я рассматриваю это как конкретный тип, например, самодельный класс A)
- Объединение итераторов
- итерируемый (это псевдотип , массив или проходимые принимаются)
- массив (это конкретный тип, и он не подлежит обмену с итератором в контексте того, что требуется тип итератора)
- ArrayIterator (может использоваться для преобразования массива в итератор)
Итак, вот почему, если функция A(итерируемая $a){}, то она принимает параметр либо массива, либо экземпляра , проходимого (Итератор, IteratorAggregate оба принимаются, потому что очевидно, что эти два класса реализуют Проходимый. В моем тесте передача ArrayIterator также работает).
В случае, если для параметра указан тип итератора, передача в массив приведет к ошибке типа.
Вы можете использовать iterator_to_array
преобразование вашей переменной в Traversable
первый:
$array = iterator_to_array((function() use ($iterable) {yield from $iterable;})());
Метод преобразования взят из комментария под этим вопросом.
Вот рабочая демонстрация.
Для случая "с возможностью итерирования в массив" кажется, что вы не можете выполнить ни одного вызова функции, и вам нужно будет либо использовать условие в своем коде, либо определить свою собственную функцию, например, так:
function iterable_to_array( iterable $iterable ): array {
if ( is_array( $iterable ) ) {
return $iterable;
}
return iterator_to_array( $iterable );
}
Для случая "от итерации к итератору" все намного сложнее. Массивы могут быть легко переведены в Traversable
с помощью ArrayIterator
. Iterator
экземпляры могут быть просто возвращены такими, какие они есть. Это оставляет экземпляры Traversable
, которые не являются экземплярами Iterator
. На первый взгляд кажется, что вы можете использовать IteratorIterator
, для чего требуется Traversable
. Однако этот класс прослушивается и не работает должным образом при предоставлении ему IteratorAggregate
, который возвращает Generator
.
Решение этой проблемы слишком длинное, чтобы публиковать его здесь, хотя я создал мини-библиотеку, содержащую обе функции преобразования:
- функция iterable_to_iterator (итерируемый $итерируемый): Итератор
- функция iterable_to_array(повторяемый $повторяемый): массив