Общее программирование

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

— Мюссер, Дэвид Р.; Степанов, Александр А., Общее программирование

Цель

  • Чтобы заставить алгоритм/метод/решение работать в общем, инкапсулируя детали о структурах данных и операциях.
  • Чтобы позволить разработчикам сосредоточиться на решении, а не различать структуры данных в реализации.

Не болтай, покажи пример

Вычислить сумму массива

function sum(arr) {
  let total = 0;
  for(let i = 0; i < arr.length; i++) {
    total += arr[i];
  }
  return total; 
}
console.log(sum([1,2,3])); //6

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

Найдите максимальное число в массиве

function max(arr) {
  let result = arr[0];
  for(let i = 1; i < arr.length; i++) {
    if(arr[i] > result) {
      result = arr[i]
    }
  }
  return max; 
}
console.log(max([1,2,3])); //3

Как мы можем сделать лучше?

Может быть много случаев, когда нам нужно пройтись по массиву и что-то сделать с его элементами. Это должно быть хорошим вариантом для инкапсуляции операции и предоставления вызывающей стороне возможности определить, что делать с каждым элементом. Вот для чего предназначено reduce. Давайте реализуем наш собственный _reduce.

function _reduce(op, initialValue) {
  return (arr) => {
    let result;
    let index = 0;
    if(initialValue != null) {
      result = initialValue;
    } else {
      result = arr[0];
      index = 1;
    }
    for(let i = index; i < arr.length; i++) {
      result = op(result, arr[i])
    }
    return result;
  }
}

Итак, теперь мы можем переписать sum и max как:

let sumWithReduce = _reduce((sum, item) => sum + item)
let maxWithReduce = _reduce((max, item) => max > item ? max : item)
console.log(sumWithReduce([1,2,3])); //6
console.log(maxWithReduce([1,2,3])); //3

Как мы можем сделать еще лучше?

В приведенной выше реализации _reduce мы абстрагировались от деталей операции и оставили в реализации только логику цикла. Однако реализация по-прежнему зависит от for и индекса массива, поэтому она работает только для массивов. Мы делаем его более общим с помощью Итераторов.

function _genericReduce(op, initialValue) {
  return (iterables) => {
    let inited, result;
    if(initialValue != null) {
      result = initialValue;
      inited = true;
    }
    for(let item of iterables) {
      if(!inited) {
        result = item;
        inited = true;
      } else {
        result = op(result, item)
      }
    }
    return result;
  }
}
let sumWithGenericReduce = _genericReduce((sum, item) => sum + item)
let maxWithGenericReduce = _genericReduce((max, item) => max > item ? max : item)
console.log(sumWithGenericReduce([1,2,3])); //6
console.log(maxWithGenericReduce([1,2,3])); //3

Теперь он работает для всех структур данных, которые являются итерируемыми. Вот заметка, которую я написал об итераторе, если вам интересно.

class Group {
  constructor(id) {
    this.id = id;
    this.members = [];
  }
  addMember(person) {
    this.members.push(person);
  }
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => ({
        value: this.members[index++],
        done: index > this.members.length
      })
    };
  }
}
class Member {
  constructor(name, salary) { 
    this.name = name;
    this.salary = salary;
  }
}

## try to find the lowest salary in the group
let group = new Group('007');
group.addMember(new Member('mike', 1000))
group.addMember(new Member('john', 2000))
group.addMember(new Member('alfred', 3000))
let lowestSalary = _genericReduce((result, member) => {
  if(member.salary < result) {
    return member.salary
  }
  return result;
},  Number.MAX_SAFE_INTEGER)(group)
console.log(lowestSalary);

Ресурс

Пример кода

Уведомление

  • Если вы хотите следить за последними новостями/статьями из серии моих блогов, пожалуйста, 「Watch」 для подписки.