Comments 15
Извеняюсь, а где операция flatMap?
Как то мало операций, да и не те, filter и map легко можно заменить на flatMap, а fold, size для ограниченный (конечных) итераторов годиться
Я всегда думал, что три столпа функционального программирования - функции высшего порядка, чистые данные и вызов-по-необходимости.
map
не изменяет оригинальный итератор, а создает новый.
А если у нас в исходной коллекции миллионы элементов и начальная коллекция после применения map нас перестанет интересовать?
Нет ли возможности применить map к исходной коллекции, не создавая ее дубля?
Аналогично для filter - не создать новую отфильтрованную коллекцию, но удалить из исходной те элементы, которые не удовлетворяют условиям фильтра.
Все это хорошо и красиво, пока вы работаете с десятком котиков. А когда вам нужно будет работать с десятком миллионов реальных данных, все это становится достаточно громоздко по памяти.
А разве есть возможность модифицировать данные «на месте». Данные в векторе лежат линейно, физически не получится положить что то большее, в теории можно положить что-то равное либо меньшее. Если с чем то равным профит реален, то с меньшим уже вопрос. Вся эта магия с переразметкой памяти уже относится к хакам и этому нет места в стандартной библиотеке.
А по filter, есть еще retain - оставляет то что нужно в текущей коллекции сохраняя порядок, но требует линейной памяти [bool].
Ну ситуации бывают разные. Я часто сталкивался с тем, что требуется именно модификация в рамках того же размера памяти. Или фильтрация когда порядок не важен, но важно уменьшение количества элементов вектора (play-off алгоритмы когда на каждом из нескольких проходов размер вектора уменьшается и время цикла на каждом проходе сокращается).
В любом случае, подобные вещи достаточно сильно зависят от задачи и не являются универсальными. Представьте что у вас в векторе хотя бы 500 000 элементов (по нашим меркам это относительно немного). И вам нужно последовательно применить map или filter 10-15-20 раз...
Нет ли возможности применить map к исходной коллекции, не создавая ее дубля?
Аналогично для filter - не создать новую отфильтрованную коллекцию, но удалить из исходной те элементы, которые не удовлетворяют условиям фильтра.
В принципе, для таких вещей давно придуманы функциональные структуры данных (описаны в книге Chris Okasaki "Pure Functional Data Structures"). Но я не знаю, как с этим обстоит дело в Rust.
Нет ли возможности применить map к исходной коллекции, не создавая ее дубля?
Это уже какой-то ограниченный map, т.к. тип на выходе должен совпадать с типом на входе.
А если у нас в исходной коллекции миллионы элементов и начальная коллекция после применения map нас перестанет интересовать?
map/filter работают с (потенциально бесконечными) итераторами, а не коллекциями.
Это уже какой-то ограниченный map, т.к. тип на выходе должен совпадать с типом на входе.
Я вам больше скажу. Бывают случаи, когда нужен не map, а action - некоторое действие с каждым элементов коллекции.
map/filter работают с (потенциально бесконечными) итераторами, а не коллекциями.
Ну это уже сферические map/filter в вакууме. В реальности все ограничивается размерами памяти и накладными расходами на ее выделение/освобождение (когда объемы данных достаточно велики и оценить заранее их не представляется возможным)
Я не спорю и не критикую, просто мысленно примеряю под те задачи, с которыми приходится сталкиваться чаще всего - насколько все это может быть удобно и применимо лично мне. Не более того.
Я вам больше скажу. Бывают случаи, когда нужен не map, а action - некоторое действие с каждым элементов коллекции.
И для этого также есть разные средства (к сожалению, в статье не показанные).
Если этот action просто использует значение (скорее, ссылку на него), то for
, который работает на всём, что реализует IntoIterator
, который включает в себя (но не ограничивается этим) и сами итераторы:
for item in &vec {
println!("Item is {item}");
}
Если же нужно in-place, то... Снова for
, потому что итерироваться можно ещё и по mut
-ссылкам:
for item in &mut vec {
// in-place увеличиваем каждый элемент на 10
*item += 10;
}
Ну это уже сферические map/filter в вакууме. В реальности все ограничивается размерами памяти и накладными расходами на ее выделение/освобождение
Вовсе нет. В этом и прекрасная особенность итераторов: они не обязаны соответствовать где-то выделенному куску данных, а являются генераторами некого потока данных, который на каждой итерации порождает как-то следующее значение, причём далеко не обязательно в дальнейшем все эти значения куда-то складывать (в статье, к сожалению, преимущественно такое показано).
И это прекрасно, потому что зачастую функции не нужно собирать все элементы в памяти, ей достаточно единожды обходимого потока данных (самый простой пример - нахождения минимума/максимума). И таким функция достаточно принимать просто некий итератор, а не фиксированный кусок памяти.
Например, в std есть iter::repeat
, который просто неограниченно генерирует один и тот же элемент, а уж что с ним будет делать получатель данных - его выбор. И при этом самому итератор нужно памяти на своё внутренне представление. Или, например, в крейте
rand
ГПСЧ позволяют получить из них итераторы, возвразающие бесконечный итератор псевдослучайных чисел.
Присоединяйтесь к бесплатному курсу по Rust на платформе Stepik :)
При использовании .into_iter()
указание ссылки в аргументе следующей closure кажется лишним. Например в обоих случаях .into_iter().filter(|&x| x % 3 == 0)
или .filter(|&&x| x % 2 == 0)
один амперсанд можно опустить.
Три столпа функционального программирования в Rust: map, filter и fold