Comments 57
Перефразируя классика, можно сказать, что все php–библиотеки нужны, все php–библиотеки важны. А если серьезно, то библиотека наверняка может быть полезна для повторяющихся специфичных задач с массивами чисел. Для единичных же случаев чаще всего можно обойтись без циклов, но с map, filter, reduce.
обратите внимание на http://www.php-fig.org/psr/psr-1/ и http://www.php-fig.org/psr/psr-2/
> $list[$list['> 0']];
о нет, только не вуду-магия (по всей видимости, с парсингом), уж лучше filter по предикату
о нет, только не вуду-магия (по всей видимости, с парсингом), уж лучше filter по предикату
если настолько сильно не нравится вуду-магия, можете использовать явный синтаксис =)
Но вообще это синтаксический сахар для того, что бы подобные конструкции занимали меньше места и были читаемыми.
$result = $list[$list->gt(25)];
Но вообще это синтаксический сахар для того, что бы подобные конструкции занимали меньше места и были читаемыми.
Если мы о «читаемом», тогда уж сделайте
$result = $list->gt(25); // [26, 78, 99]
Конечно, такой вариант я рассматривал.но:
1. это противоречит подходу, реализованному в numpy библиотеки и этому есть веская причина:
2. $list->gt(25) вернёт массив, называемый булевой маской: [false, false, true, true, false, true], где false — элемент в этой позиции НЕ удовлетворяет условию, true — удовлетворяет.
Преимущество такого подхода заключается в том, что такие булевые маски позволяют очень гибко выбрать элементы из массив и в дополнение их можно комбинировать и делать их логическое объединение:
к моему сожалению, в php нельзя переопределить операторы, иначе синтаксис получился бы значительно красивее:
1. это противоречит подходу, реализованному в numpy библиотеки и этому есть веская причина:
2. $list->gt(25) вернёт массив, называемый булевой маской: [false, false, true, true, false, true], где false — элемент в этой позиции НЕ удовлетворяет условию, true — удовлетворяет.
Преимущество такого подхода заключается в том, что такие булевые маски позволяют очень гибко выбрать элементы из массив и в дополнение их можно комбинировать и делать их логическое объединение:
$result = $list[operator::b_and($list->gte(5), $list->lt(8))];
// or
$result = $list[operator::b_and($list['>= 5'], $list['< 8'])];
к моему сожалению, в php нельзя переопределить операторы, иначе синтаксис получился бы значительно красивее:
$list[$list > 25];
// and
$result = $list[($list >= 5) & ($list < 8)];
не достигается ли этот же вариант битовых масок обычной композицией функций?
function gt($b) {
return function ($a) use ($b) {
return $a > $b;
};
}
function lt($b) {
return function ($a) use ($b) {
return $a < $b;
};
}
function all(\Closure ...$filters) {
return function (...$args) use ($filters) {
foreach ($filters as $filter) {
if (!$filter(...$args)) {
return false;
}
}
return true;
};
}
function any(\Closure ...$filters) {
return function (...$args) use ($filters) {
foreach ($filters as $filter) {
if ($filter(...$args)) {
return true;
}
}
return false;
};
}
$arr = range(0, 40);
array_filter($arr, any(lt(2), all(gt(25), lt(30)))); // 0, 1, 26, 27, 28, 29
может быть я не учитываю каких-то юзкейсов конечно… ну и да, понятно что php для подобного пока не самый удобный язык.
по сути, часть библиотеки и представляет собой отдельные методы-хелперы, которые потом можно компоновать как угодно, так как они возвращают одинаковые типы данных: np_array в данным случае.
методы all, any, кстати, в ближайших планах тоже ;)
методы all, any, кстати, в ближайших планах тоже ;)
да, ещё. булевые маски полезны в случае если нужно выбрать более гибко по индексам. К примеру из массива из 10 элементов выбрать только 1ый, 3-ий, 6-ой и 9-ый элементы.
$list[[false, true, false, true, false, false, true, false, false, true, false]];
$list[[false, true, false, true, false, false, true, false, false, true, false]];
К примеру из массива из 10 элементов выбрать только 1ый, 3-ий, 6-ой и 9-ый элементы.
в php это ограничение array_filter которое не предоставляет доступа к ключам. В JS том же я вполне могу сделать так:
arr.filter(byKey(in(1, 3, 6, 9))
$resultArrray = $sourceArray[$something];
С точки зрения читаемости кода и синтаксиса php — это обращение к конкретному элементу массива по индексу. Вы же вместо индекса используете условие или результат выполнения метода. Это конечно не верх нечитаемости, но близко к тому. Хуже может быть только нагромождение тернарщины, которая тоже по сути своей является синтаксическим сахаром, но её применять тоже нужно с умом.
Ваше решение имеет право на жизнь конечно, и вполне допускаю, что кому-либо оно будет удобно и читаемо, но далеко не всем.
О да, стало намного читабельнее…
эх был бы пайп оператор в пыхе...
use function myCooolLib\Comparsion\gt;
use function \array_filter as filter
$arr = range(10, 30)
|> filter($$, gt(25)); // 26, 27, 28, 29, 30
а вообще есть интересная RFC на эту тему: operator functions. Но вот только я бы для начала пофиксил способ задания ссылок на функции.
мне больше бы импонировао, если бы приняли вот это: wiki.php.net/rfc/operator-overloading
PIPE оператор можно реализовать через HOM прокси (типа такого) с доступом к глобальным функциям. Получаем:
use function \array_filter as filter
wrapper(range(10, 30))
->filter(_, gt(25));
Вместо кода
можно написать так:
$result = [];
for($i=0; $i<count($list); $i++)
if ($list[$i] >= 0)
$result[] = $list[$i];
можно написать так:
$result = array_filter($list, function($v) { return $v >= 0; });
Могу согласиться с тем, что такой синтаксис непривычен. Но тут, как всегда, дело контекста.
если вы работаете в проекте где все знают что используется либа numphp и мы с ней знакомы, то проблем не будет. Более того, IDE подсветит что это объект.
Опять же, если кому крайне непривычно, можно использовать явный синтаксис: $list->gt(0);
если вы работаете в проекте где все знают что используется либа numphp и мы с ней знакомы, то проблем не будет. Более того, IDE подсветит что это объект.
Опять же, если кому крайне непривычно, можно использовать явный синтаксис: $list->gt(0);
for($i=0; $i<count($list); $i++)
Месье не слышал о цикле foreach?
Я за такое студентам сразу «кол» ставлю. Рекомендую повторять вслух, как мантру, до полного просветления: «индексы массивов в PHP не обязаны быть плотны, монотонны и вообще не обязаны быть числами»
foreach, равно как и прочие циклы не всегда хороши при обходе массива. Есть же array_walk, array_filter и иже с ними.
Если сравнивать foreach и for в PHP — вы что предпочтете?
Зависит от исходных данных и условий. Для простого прямого обхода массива foreach однозначно проще и удобнее. Для индексированного числами в строгом порядке массива, для выборки элементов с определенным шагом — почему бы не использовать for?
Почему? Потому что в языке нет инструмента, гарантирующего, что ваш массив так и останется «индексированным числами в строгом порядке»
for для обхода массивов — логическая бомба, которую вы подкладываете в свой код
for для обхода массивов — логическая бомба, которую вы подкладываете в свой код
Потому что в языке нет инструмента, гарантирующего, что ваш массив так и останется «индексированным числами в строгом порядке»
Почему же? Для массивов в пыхе есть массивы, а не только хеш-мапы.
SplFixedArray не гарантирует плотность индексов.
Почему?
Заголовок спойлера
$s = new \SplFixedArray(10);
$s[0] = 23;
$s[2] = 42;
for ($i = 0, $len = \count($s); $i < $len; ++$i) {
echo \gettype($s[$i]) . "\n";
}
/**
integer
NULL
integer
NULL
NULL
NULL
NULL
NULL
NULL
NULL
**/
Каждой задаче — свой инструмент. Ставить «кол» студентам за использование for — не логично. Логично уточнить почему был выбран for|foreach|while|array_walk|etc.
Отчего же. Совершенно логично.
Цикл for не проходится намеренно. Если он встречается в ДЗ, это значит, что ДЗ было списано, причем было списано совершенно бездумно, поскольку на лекции несколько раз повторяется, почему нужно использовать именно foreach
Цикл for не проходится намеренно. Если он встречается в ДЗ, это значит, что ДЗ было списано, причем было списано совершенно бездумно, поскольку на лекции несколько раз повторяется, почему нужно использовать именно foreach
Вопрос не имеет отношения к теме ветки дискуссии.
Ровно также «никак», как и цикл for.
Ровно также «никак», как и цикл for.
Имеется ввиду это?
foreach($items as $i => $value) {
if ($i % 100 === 0) { yield $value; }
}
Это похоже не дело принципа =) обход массива с заданным шагом — классическая задача for массива. это значительно более читабельно чем $i % 100. более того, foreach будет проходить и сравнивать каждый элемент, хотя это не оптимально, а for не тратит время на это, более того, не создаёт $value переменную каждый раз, тратя на это ресурсы.
А с чем тут спорить-то? Я совершенно точно знаю, что если встретил for для обхода массива — это говнокод, который подсказали студенту где-нибудь на «Ответах Mail.ru», поскольку оператор for принципиально не озвучивается на лекциях.
В чем предмет спора?
В чем предмет спора?
В чем я неправ? В том, что в общем случае цикл for неприменим для перебора элементов массивов в PHP?
Вы это серьезно?
— примените здесь цикл for, пожалуйста. Хотя бы для того, чтобы подсчитать сумму количества дней во всех месяцах.
И тогда я немедленно признаю, что был неправ.
Вы это серьезно?
$days = [
'jan' => 31,
'feb' => 28,
'mar' => 31,
];
— примените здесь цикл for, пожалуйста. Хотя бы для того, чтобы подсчитать сумму количества дней во всех месяцах.
И тогда я немедленно признаю, что был неправ.
По-вашему, студент не может нигде получить информацию кроме ваших лекций? Или может вы считаете, что до ваших лекций ни один студент не мог изучать другой язык. где есть for?
1) $value не создаётся каждый раз. Напомню, что выделение zval структуры происходит единожды и при следующем «тике» итерации, если этот контейнер не был задействован, то он переиспользуется. За счёт этого оптимизируются тяжёлые операции аллоцирования памяти. Помимо этого использование корутинки однозначно указывает на то, что в памяти может находиться только один контейнер для $value в один момент времени.
2) Получение элемента по индексу хеш-мапы — это, если ничего не путаю, операция O(n), а вызов next() итератора — это O(1). По-этому, в сумме, цикл for будет жрать O(log n), а foreach по всем элементам O(n). Кажется, я ничего не перепутал =)
3) Ну и тривиальный косяк с тем, кто в хеш-мапе (массиве пыха) индексы не упорядочены, а значит надо либо итерироваться по внутреннему итератору «массива» (функции next), либо делать slice, либо затратить тот же самый O(n) для сброса ключей через array_value
Кажется всё логично? Написал свои пунктики выше, проверил специально, и… Нет, нифига, for всё равно быстрее. Расскажете где я ошибся в своих выводах? А то, признаться, хз.
2) Получение элемента по индексу хеш-мапы — это, если ничего не путаю, операция O(n), а вызов next() итератора — это O(1). По-этому, в сумме, цикл for будет жрать O(log n), а foreach по всем элементам O(n). Кажется, я ничего не перепутал =)
3) Ну и тривиальный косяк с тем, кто в хеш-мапе (массиве пыха) индексы не упорядочены, а значит надо либо итерироваться по внутреннему итератору «массива» (функции next), либо делать slice, либо затратить тот же самый O(n) для сброса ключей через array_value
Кажется всё логично? Написал свои пунктики выше, проверил специально, и… Нет, нифига, for всё равно быстрее. Расскажете где я ошибся в своих выводах? А то, признаться, хз.
Заголовок спойлера
P.S. буферизацию добил, дабы случайно на DCE оптимизацию не нарваться.
// foreach
function a(iterable $items): iterable
{
foreach ($items as $i => $value) {
if ($i % 100 === 0) {
yield $value;
}
}
}
// for
function b(iterable $items): iterable
{
$array = \array_values(\is_array($items) ? $items : \iterator_to_array($items));
for ($i = 0, $len = \count($array); $i < $len; $i += 100) {
yield $array[$i];
}
}
$items = \range(10, 100000);
// foreach tests (~0.050s)
$before = \microtime(true);
foreach (a($items) as $i) {
\ob_start();
\var_dump($i);
\ob_end_clean();
}
echo \number_format(\microtime(true) - $before, 5) . 's' . "\n";
// for tests (~0.005s)
$before = \microtime(true);
foreach (b($items) as $i) {
\ob_start();
\var_dump($i);
\ob_end_clean();
}
echo \number_format(\microtime(true) - $before, 5) . 's' . "\n";
P.S. буферизацию добил, дабы случайно на DCE оптимизацию не нарваться.
Ну так в вашей цитате как раз и написано, что это всё хешмапы, а к ним доступ O(n). Откуда O(1)?
Я изначально не правильно посчитал алгоритм получения элемента из хешмапы. Нашёл статью, просвятился, понял где косяк, спасибо )
Ну как экспериментально-учебно-пет-проект пойдёт.
Но на практике — не взлетит. Не видно ни одного преимущества перед стандартными filter/map/etc — ни по удобству синтаксиса, ни по быстродействию.
Но на практике — не взлетит. Не видно ни одного преимущества перед стандартными filter/map/etc — ни по удобству синтаксиса, ни по быстродействию.
Если кому интересно, я добавил базовую поддержку работы с матрицами в библиотеку: numphp (раздел readme про матрицы).
Кратко:
Ну, и, конечно, различные комбинации. Например: максимальное из всех чисел в матрице, меньше 6
Чуть позже будет подробная запись, когда доработаю остальные методы по работе с матрицами, типа суммирование, нахождение среднего, генераторы и так далее.
Кратко:
$matrix = new np_array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]);
// matrix
[[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]]
$result = $matrix->mul(5);
//result
[[0, 5, 10, 15],
[20, 25, 30, 35],
[40, 45, 50, 55]]
Ну, и, конечно, различные комбинации. Например: максимальное из всех чисел в матрице, меньше 6
$result = $matrix[$matrix->lt(6)]->max();
Чуть позже будет подробная запись, когда доработаю остальные методы по работе с матрицами, типа суммирование, нахождение среднего, генераторы и так далее.
Sign up to leave a comment.
Numphp — библиотека для работы с числовыми массивами