Что делает reduce в программировании
Перейти к содержимому

Что делает reduce в программировании

  • автор:

Что делает метод reduce python?

reduce — это функция из встроенного модуля functools .

Сигнатура функции: reduce(function, iterable[, initializer]) .

Принимает функцию для двух аргументов, итерабельный объект и необязательный initializer — аккумулирующее значение (по умолчанию равен первому элементу iterable).

from functools import reduce reduce(lambda x, y: x + y, range(4), 3) # то же самое что (((3+1)+2)+3) # 9 def get_maximum(first_num, second_num): return first_num if first_num > second_num else second_num reduce(get_maximum, [2, 3, 5, 4, 1]) # 5 

Функция reduce() в Python

Функция reduce() модуля functools кумулятивно применяет функцию function к элементам итерируемой iterable последовательности, сводя её к единственному значению.

Аргумент function это функция которую требуется применить к элементам последовательности. Должна принимать два аргумента, где первый аргумент — аккумулированное ранее значение, а второй аргумент следующий элемент последовательности.

Аргумент iterable представляет собой последовательность, элементы которой требуется свести к единственному значению. Если последовательность пуста и не задан аргумент initializer , то возбуждается исключение TypeError .

Например reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) вычисляет ((((1 + 2) +3) +4) +5) . Левый аргумент x — это накопленное значение, а правый аргумент y — это следующий элемент iterable .

Если присутствует необязательный initializer , он помещается перед элементами iterable в вычислении. Другими словами это базовое значение, с которого требуется начать отсчёт. Аргумент initializer , так же служит значением по умолчанию, когда iterable является пустым.

Функция reduce() эквивалентна следующему коду:

def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value 
Примеры использования:

Вычисление суммы всех элементов списка при помощи reduce:

>>> from functools import reduce >>> items = [10, 20, 30, 40, 50] >>> sum_all = reduce(lambda x,y: x + y, items) >>> sum_all # 150 

Вычисление наибольшего элемента в списке при помощи reduce:

>>> from functools import reduce >>> items = [1, 24, 17, 14, 9, 32, 2] >>> all_max = reduce(lambda a,b: a if (a > b) else b, items) >>> all_max # 32 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Способы использования модуля functools
  • Декоратор @cached_property модуля functools
  • Функция cmp_to_key() модуля functools
  • Декоратор @cache() модуля functools, кеширующий декоратор
  • Декоратор @lru_cache() модуля functools
  • Декоратор @total_ordering модуля functools
  • Функция partial() модуля functools
  • Класс partialmethod() модуля functools
  • Функция reduce() модуля functools
  • Декоратор @singledispatch модуля functools
  • Декоратор @singledispatchmethod модуля functools
  • Декоратор @update_wrapper() модуля functools
  • Декоратор @wraps() модуля functools

reduce

func — Функция, которую требуется применить к элементам последовательности. Должна принимать два аргумента, где первый аргумент — аккумулированное ранее значение, а второй — следующий элемент последовательности.

iterable — Последовательность, элементы которой требуется свести к единственному значению. Если последовательность пуста и не задан initializer, то возбуждается TypeError.

initializer=None — Базовое значение, с которого требуется начать отсчёт. Оно же будет возвращено, если последовательность пуста.

 def reducer_func(el_prev, el): 
# el_prev - предшествующий элемент
# el - текущий элемент
return el_prev + el

# python 2
reduce(reducer_func, [1, 2, 3]) # 6

# python 3
from functools import reduce
reduce(reducer_func, [1, 2, 3]) # 6

На заметку

Python 3 Настоятельно рекомендуется использовать обычный проход по элементам при помощи for для повышения читаемости кода. Если функция всё же требуется, то её можно найти в functools.reduce().

Как работает reduce() в JavaScript, когда его нужно применять и какие крутые вещи можно с ним делать

В этой статье вы узнаете про метод reduce() и какие клевые штуки можно вытворять с его помощью, выйдя за рамки его общеизвестного применения.

��Мой Твиттер — там много из мира фронтенда, да и вообще поговорим��. Подписывайтесь, будет интересно: ) ✈️

reduce() в JavaScript это один из краеугольных камней функционального программирования. Давайте изучим то, как он работает, когда его нужно применять и то какие крутые вещи можно с его помощью вытворять.

Простая редукция

Когда использовать: когда у вас есть массив чисел и вам надо всех их сложить.

const euros = [29.76, 41.85, 46.5];
const sum = euros.reduce((total, amount) => total + amount);
sum // 118.11
  • В этом примере reduce() принимает два параметра, total и число с которым сейчас идёт работа.
  • Метод проходится по каждому числу в массиве, как бы это было с циклом for .
  • Когда цикл только начинается, total имеет значение первого числа с начала массива (29.76), а числом в обработке становится следующее по этому же массиву число (41.85).
  • Конкретно в этом примере, нам надо прибавить настоящее число к total .
  • Такое вычисление повторяется для каждого числа в массиве и каждый раз настоящее число меняется на следующее число в массиве справа.
  • Когда уже нет чисел в массиве, метод отдаёт значение total .

ES5 версия JavaScript метода reduce

Если вы до этого никогда не использовали ES6 синтаксис , то не дайте примеру выше испугать вас. Это тоже самое, что написано ниже:

var euros = [29.76, 41.85, 46.5]; 
var sum = euros.reduce( function(total, amount) return total + amount
>);
sum // 118.11

Мы используем const вместо var и мы заменим слово function на => (стрелочная функция) после параметров, а ещё мы не будем писать слово return .

Я буду писать на ES6 синтаксисе в оставшихся примерах, так как это коротко и оставляет меньше пространства для возникновения ошибок.

Находим среднее число с JavaScript методом reduce

Тут вместо вывода суммы вам надо разделить её на длину массива перед тем, как вы вернете последнее значение.

Вы можете это сделать воспользовавшись преимуществами других аргументов доступных в reduce() . Первый из них это index . Как и с циклом for , index показывает количество раз, которое редюсер пробежался по массиву. Последний аргумент это уже сам массив.

const euros = [29.76, 41.85, 46.5];
const average = euros.reduce((total, amount, index, array) => total += amount;
if( index === array.length-1) <
return total/array.length;
>else <
return total;
>
>);
average // 39.37

Map и Filter как редюсеры

Если вы можете использовать функцию reduce , чтобы найти среднее число, то следовательно, вы можете использовать её любым образом, в принципе, каким только захотите.

Для примера, вы могли бы увеличить вдвое total или разделить на два каждое число перед тем, как сложить их вместе, ну или вы бы могли использовать if в редукторе для того, чтобы сложить числа, которые больше 10. Я считаю, что в JavaScript метод reduce даёт вам что-то вроде мини CodePen, в котором вы можете записать совершенно любую логику. Он просто пропустит её через каждое число в массиве и отдаст одно значение.

Но фишка в том, что вам не обязательно отдавать только одно значение. Вы можете заредюсить массив в новый массив.

Для примера, давайте сократим массив в другой массив, в котом каждое число будет увеличено вдвое. Для этого нам надо выставить изначальное значение для аккумулятора на пустой, уже изначальный массив.

Изначальное значение — это значение параметра total при старте функции. Вы можете выставить это значение добавив запятую, за которой следует изначальное значение внутри круглых скобок, но после фигурных скобок (выделено жирным в примере ниже).

const average = euros.reduce((total, amount, index, array) => total += amount 
return total/array.length
>, 0);

В предыдущих примерах исходным значением был ноль, поэтому я просто пропускал его. Пропуская указание изначального значения, total будет по дефолту получать значение первого числа в массиве.

Указывая исходное значение, как пустой массив, мы можем в последствии добавлять каждое значение в total . Если мы хотим с помощью reduce превратить массив значений в ещё один массив, в котором каждое значение будет умножено на два, то нам нужно добавлять amount * 2 . А затем отдавать total , когда уже не останется чисел для добавления.

const euros = [29.76, 41.85, 46.5];
const doubled = euros.reduce((total, amount) => total.push(amount * 2);
return total;
>, []);
doubled // [59.52, 83.7, 93]

Мы создали новый массив, в котом каждое число умножается на два. Мы могли бы также отфильтровать числа, которые нам не нужны, просто добавив if внутри функции.

const euro = [29.76, 41.85, 46.5];
const above30 = euro.reduce((total, amount) => if (amount > 30) total.push(amount);
>
return total;
>, []);
above30 // [ 41.85, 46.5 ]

В общем, это как методы map() и filter() , только переписанные в reduce() .

Но для этих примеров было бы разумнее использовать map или filter , так как их попросту визуально легче воспринимать. Преимущество метода reduce становится очевидным, когда вам надо сделать map и filter вместе, и при этом у вас довольно большие объемы данных для обработки.

При создании цепочки с map и filter , получается то, что вы делаете одну и ту же работу дважды. Вы отфильтровываете каждое значение и затем вы пробегаетесь с указанными параметрами функции по каждому оставшемуся из них. А с reduce вы можете отфильтровать и пробежаться по всему массиву за один подход.

Используйте map и filter , но когда вы начнете выстраивать цепочку с множеством методов, то помните, что куда быстрее с этими данными применить reduce .

Ведём учёт данных с помощью reduce

Когда использовать: когда у вас есть коллекция данных и вам надо узнать то, сколько типов каждого элемента находится в этой коллекции.

const fruitBasket = ['banana', 'cherry', 'orange', 'apple', 'cherry', 'orange', 'apple', 'banana', 'cherry', 'orange', 'fig' ];const count = fruitBasket.reduce( (tally, fruit) => tally[fruit] = (tally[fruit] || 0) + 1 ; 
return tally;
> , <>)
count //

Чтобы подсчитать количество элементов в массиве, изначальным значением должен быть пустой объект, а не массив, как было в предыдущем примере.

Так как мы собираемся возвращать объект, то мы теперь можем хранить пару ключ-значение в total .

fruitBasket.reduce( (tally, fruit) => tally[fruit] = 1; 
return tally;
>, <>)

Сначала нам надо выдать имя первому ключу, назвав его как наше первое значение и затем дать ему значение 1 .

Это даст нам объект, где все названия фруктов будут в виде ключей и каждый будет иметь значение 1 . А нам нужно увеличить значение каждого фрукта, если он повторяется.

Для этого, на втором цикле мы проверяем есть ли в total ключ с данным фруктом. Если нет, то мы создаем данный ключ. А если есть, то мы увеличиваем его значение на единицу.

fruitBasket.reduce((tally, fruit) => if (!tally[fruit]) tally[fruit] = 1; 
> else tally[fruit] = tally[fruit] + 1;
>
return tally;
>, <>);

Я описал такую же логику, но более сжатым способом, который вы видели немного выше.

Сливаем массив воедино с помощью reduce

Мы можем использовать reduce , чтобы сливать воедино вложенные значения, которые в конечном итоге обретут форму единого массива.

Мы выставляем изначальное значение на пустой массив и далее конкатенируем данное значение с total .

const data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const flat = data.reduce((total, amount) => return total.concat(amount);
>, []);
flat // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Куда чаще информация вложена более сложными способами. Для примера, давайте представим, что нам надо просто получить все цвета в переменной data , находящейся ниже.

const data = [ 
,
,

];

Мы пройдемся по каждому объекту и возьмём оттуда нужные цвета. Это мы сделаем, просто указав forEach пробежаться по amount.c , где при каждой итерации, вложенный массив будет добавляться в total .

const colors = data.reduce((total, amount) => amount.c.forEach( color => total.push(color); 
>)
return total;
>, [])
Цвета
//['blue','green','green','black','orange','blue','green','red']

Если нам понадобятся только уникальные значения, то тогда нам надо будет проверить есть ли заданное значение в total , прежде чем отправлять его в этот массив.

const uniqueColors = data.reduce((total, amount) => amount.c.forEach( color => if (total.indexOf(color) === -1) total.push(color); 
>
>);
return total;
>, []);
uniqueColors // [ 'blue', 'red', 'green', 'black', 'orange']

Пайплайн с reduce()

Довольно интересным моментом в методе reduce() является то, что вы можете можете работать с функциями, как с числами и строками.

Предположим, что у нас есть набор простых математических функций, с помощью которых мы можем прибавлять, убавлять, умножать на два и делить на два значение amount .

function increment(input) function decrement(input) function double(input) function halve(input) < return input / 2; >

В общем, дальше нам надо будет увеличить значение на 1 , потом умножить на два, потом вычесть единицу.

Конечно, мы бы могли написать функцию, которая берёт input и возвращает (input + 1) * 2 -1 . Но тут проблема в том, что мы знаем то, что собираемся увеличить значение, потом удвоить его и потом вычесть единицу и затем разделить его на два, в какой-нибудь момент в будущем. Мы не хотим переписывать нашу функцию каждый раз, так что мы сделаем пайплайн с помощью reduce() .

Пайплайн (pipeline) это термин, который используется для набора функций, которые последовательно изменяют какое-либо значение в уже финальный результат. Наш пайплайн будет состоять из трех функций, в том порядке, в котором мы хотим их использовать.

let pipeline = [increment, double, decrement];

В общем, теперь мы используем reduce() не на массиве значений, а на пайплайне из функций. Такая конструкция сработает, потому что мы выставили изначальное значение на то, которое хотим в дальнейшем трансформировать.

const result = pipeline.reduce(function(total, func) return func(total);
>, 1);
result // 3

Так как пайплайн у нас является массивом, то он может быть легко модифицирован. Если мы хотим что-нибудь увеличить на 1 три раза подряд, потом вдвое увеличить, вычесть 1 и разделить на два, то нам будет достаточно просто изменить пайплайн.

var pipeline = [ 
increment,
increment,
increment,
double,
decrement,
halve
];

Сама функция останется такой же.

Избегаем глупых ошибок

Если вы не передадите изначальное значение, то reduce() будет считать, что им является первый элемент в массиве. Всё работало отлично в первых примерах, потому что мы работали со списком чисел.

Если вы захотите подсчитать фрукты и упустите изначальное значение, то ничего у вас тогда не выйдет. Вообще, упущение момента указания изначального значения это ошибка, которую легко пропустить и именно её нужно проверять первым делом при дебаггинге.

Ещё частенько забывают отдавать total . Вы должны возвращать какое-нибудь значение, что reduce() продолжал работать. Всегда проверяйте дважды и убеждайтесь в том, что вы отдаёте нужное вам значение.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *