Pull to refresh

Comments 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 — это обращение к конкретному элементу массива по индексу. Вы же вместо индекса используете условие или результат выполнения метода. Это конечно не верх нечитаемости, но близко к тому. Хуже может быть только нагромождение тернарщины, которая тоже по сути своей является синтаксическим сахаром, но её применять тоже нужно с умом.
Ваше решение имеет право на жизнь конечно, и вполне допускаю, что кому-либо оно будет удобно и читаемо, но далеко не всем.
Здесь нужно понимать, что это — отдельный тип данных, не классический массив.
И если немного расширить понятие индексации, то тут — обращение к конкретным элементам. Если так воспринимать, тогда не возникает недоразумений
UFO landed and left these words here
О да, стало намного читабельнее…

эх был бы пайп оператор в пыхе...


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 раз в день с разными условиями и так далее. Массажирование данных, так сказать.
UFO landed and left these words here
Могу согласиться с тем, что такой синтаксис непривычен. Но тут, как всегда, дело контекста.
если вы работаете в проекте где все знают что используется либа numphp и мы с ней знакомы, то проблем не будет. Более того, IDE подсветит что это объект.
Опять же, если кому крайне непривычно, можно использовать явный синтаксис: $list->gt(0);
UFO landed and left these words here
В любом случае, $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
UFO landed and left these words here
Вопрос не имеет отношения к теме ветки дискуссии.
Ровно также «никак», как и цикл for.
Это похоже не дело принципа =) обход массива с заданным шагом — классическая задача for массива. это значительно более читабельно чем $i % 100. более того, foreach будет проходить и сравнивать каждый элемент, хотя это не оптимально, а for не тратит время на это, более того, не создаёт $value переменную каждый раз, тратя на это ресурсы.
UFO landed and left these words here
А с чем тут спорить-то? Я совершенно точно знаю, что если встретил for для обхода массива — это говнокод, который подсказали студенту где-нибудь на «Ответах Mail.ru», поскольку оператор for принципиально не озвучивается на лекциях.
В чем предмет спора?
UFO landed and left these words here
В чем я неправ? В том, что в общем случае цикл for неприменим для перебора элементов массивов в PHP?
Вы это серьезно?

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

— примените здесь цикл for, пожалуйста. Хотя бы для того, чтобы подсчитать сумму количества дней во всех месяцах.
И тогда я немедленно признаю, что был неправ.
UFO landed and left these words here
примените здесь цикл 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 оптимизацию не нарваться.
UFO landed and left these words here
UFO landed and left these words here
Ну так в вашей цитате как раз и написано, что это всё хешмапы, а к ним доступ O(n). Откуда O(1)?
UFO landed and left these words here
Я изначально не правильно посчитал алгоритм получения элемента из хешмапы. Нашёл статью, просвятился, понял где косяк, спасибо )
Ну как экспериментально-учебно-пет-проект пойдёт.
Но на практике — не взлетит. Не видно ни одного преимущества перед стандартными 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();


Чуть позже будет подробная запись, когда доработаю остальные методы по работе с матрицами, типа суммирование, нахождение среднего, генераторы и так далее.
Sign up to leave a comment.

Articles