Обновить

Комментарии 57

Перефразируя классика, можно сказать, что все php–библиотеки нужны, все php–библиотеки важны. А если серьезно, то библиотека наверняка может быть полезна для повторяющихся специфичных задач с массивами чисел. Для единичных же случаев чаще всего можно обойтись без циклов, но с map, filter, reduce.
Совершенно верно. Особенно важно учитывать что в задачах связанных с data mining, machine learning, deep learning, такие вещи повторяюсь из раза в раз и тратить время рутинные циклические операции не стоит вообще
да, спасибо большое. Это имеет место быть. В последнее время пишу на python и там другие соглашения и глаз уже радуется python-style, а php-style не нравится. НО раз это php либа — нужно следовать psr )
> $list[$list['> 0']];
о нет, только не вуду-магия (по всей видимости, с парсингом), уж лучше 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 — удовлетворяет.

Преимущество такого подхода заключается в том, что такие булевые маски позволяют очень гибко выбрать элементы из массив и в дополнение их можно комбинировать и делать их логическое объединение:

$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, кстати, в ближайших планах тоже ;)
да, ещё. булевые маски полезны в случае если нужно выбрать более гибко по индексам. К примеру из массива из 10 элементов выбрать только 1ый, 3-ий, 6-ой и 9-ый элементы.

$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))
в php это ограничение array_filter которое не предоставляет доступа к ключам.

Чуток не понял. Коллбек array_filter принимает как ключи, так и значения. О каких ограничениях речь?

да но согласись, с флагом это будет не так удобно, хотя думаю всеравно нужна будет свое обертка.

$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. Но вот только я бы для начала пофиксил способ задания ссылок на функции.

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; });
можно. но при работе в области data science, machine learning такие операции нужно делать по 100 раз в день с разными условиями и так далее. Массажирование данных, так сказать.
НЛО прилетело и опубликовало эту надпись здесь
Могу согласиться с тем, что такой синтаксис непривычен. Но тут, как всегда, дело контекста.
если вы работаете в проекте где все знают что используется либа numphp и мы с ней знакомы, то проблем не будет. Более того, IDE подсветит что это объект.
Опять же, если кому крайне непривычно, можно использовать явный синтаксис: $list->gt(0);
НЛО прилетело и опубликовало эту надпись здесь
В любом случае, $list['>0'] — малая часть возможностей библиотеки =)
Математические операции над векторами — частая задача.
В ближайших планах — быстрый и удобный способ получить среднее значение, медиану или сумму всех элементов. $list->mean() и погнал дальше.
for($i=0; $i<count($list); $i++)
Месье не слышал о цикле foreach?
Я за такое студентам сразу «кол» ставлю. Рекомендую повторять вслух, как мантру, до полного просветления: «индексы массивов в PHP не обязаны быть плотны, монотонны и вообще не обязаны быть числами»
foreach, равно как и прочие циклы не всегда хороши при обходе массива. Есть же array_walk, array_filter и иже с ними.
Если сравнивать foreach и for в PHP — вы что предпочтете?
Зависит от исходных данных и условий. Для простого прямого обхода массива foreach однозначно проще и удобнее. Для индексированного числами в строгом порядке массива, для выборки элементов с определенным шагом — почему бы не использовать 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
**/

Потому.
<?php
$splf = SplFixedArray::fromArray([3 => 33,22,4 => 44]);
var_dump($splf);
Каждой задаче — свой инструмент. Ставить «кол» студентам за использование for — не логично. Логично уточнить почему был выбран for|foreach|while|array_walk|etc.
Отчего же. Совершенно логично.
Цикл for не проходится намеренно. Если он встречается в ДЗ, это значит, что ДЗ было списано, причем было списано совершенно бездумно, поскольку на лекции несколько раз повторяется, почему нужно использовать именно foreach
НЛО прилетело и опубликовало эту надпись здесь
Вопрос не имеет отношения к теме ветки дискуссии.
Ровно также «никак», как и цикл for.
Это похоже не дело принципа =) обход массива с заданным шагом — классическая задача for массива. это значительно более читабельно чем $i % 100. более того, foreach будет проходить и сравнивать каждый элемент, хотя это не оптимально, а for не тратит время на это, более того, не создаёт $value переменную каждый раз, тратя на это ресурсы.
НЛО прилетело и опубликовало эту надпись здесь
А с чем тут спорить-то? Я совершенно точно знаю, что если встретил for для обхода массива — это говнокод, который подсказали студенту где-нибудь на «Ответах Mail.ru», поскольку оператор for принципиально не озвучивается на лекциях.
В чем предмет спора?
НЛО прилетело и опубликовало эту надпись здесь
В чем я неправ? В том, что в общем случае цикл for неприменим для перебора элементов массивов в PHP?
Вы это серьезно?

$days = [
'jan' => 31,
'feb' => 28,
'mar' => 31,
];

— примените здесь цикл for, пожалуйста. Хотя бы для того, чтобы подсчитать сумму количества дней во всех месяцах.
И тогда я немедленно признаю, что был неправ.
НЛО прилетело и опубликовало эту надпись здесь
примените здесь цикл for, пожалуйста

Пожалуйста, цикл for:


for ($sum = 0, $i = 0, $values = array_values($days), $cnt = count($values); $i < $cnt; $i++)
    $sum += $values[$i];
По-вашему, студент не может нигде получить информацию кроме ваших лекций? Или может вы считаете, что до ваших лекций ни один студент не мог изучать другой язык. где есть 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 всё равно быстрее. Расскажете где я ошибся в своих выводах? А то, признаться, хз.

Заголовок спойлера
// 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 — ни по удобству синтаксиса, ни по быстродействию.
Если кому интересно, я добавил базовую поддержку работы с матрицами в библиотеку: numphp (раздел readme про матрицы).
Кратко:

$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();


Чуть позже будет подробная запись, когда доработаю остальные методы по работе с матрицами, типа суммирование, нахождение среднего, генераторы и так далее.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации