Три строки кода для реализации каррирования JS

внешний интерфейс алгоритм JavaScript

В последнее время я видел какие-то каррирующие статьи, как бы это сказать, очень странные ощущения. Один из Alibaba Cloudперевод, конец статьи дает такой "карри":

function curry(fn, ...args) {
    return (..._arg) => {
        return fn(...args, ..._arg);
    }
}

Автор наглядно показал разницу между каррированием и частичными приложениями, в конце он сказал, что надо реализовать каррирование, а потом написал частичное приложение... Слишком фейк, не мог не прокомментировать:

Потом я увидел нашу группу Huan Brother сегоднястатья, если честно, после прочтения кода в начале у меня не хватило терпения прочитать следующий конкретный анализ:

// 定义占位符
var _ = '_';

function magician3 (targetfn, ...preset) {
  var numOfArgs = targetfn.length;
  var nextPos = 0; // 下一个有效输入位置的索引,可以是'_',也可以是preset的结尾

  // 查看是否有足够的有效参数
  if (preset.filter(arg=> arg !== _).length === numOfArgs) {
    return targetfn.apply(null, preset);
  } else {
    // 返回'helper'函数
    return function (...added) {
      // 循环并将added参数添加到preset参数
      while(added.length > 0) {
        var a = added.shift();
        // 获取下一个占位符的位置,可以是'_'也可以是preset的末尾
        while (preset[nextPos] !== _ && nextPos < preset.length) {
          nextPos++
        }
        // 更新preset
        preset[nextPos] = a;
        nextPos++;
      }
      // 绑定更新后的preset
      return magician3.call(null, targetfn, ...preset);
    }
  }
}

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

Прежде чем мы реализуем это сами, студенты, которые не имеют понятия о каррировании, могут посмотреть на это.wiki(Посмотрите на английскую вики, объяснение каррирования в китайской вики беспорядочно и неточно, и его легко спутать с некоторыми приложениями), короче говоря, каррирование — это преобразование многопараметрической функции в серию функций, которые принимают одиночный параметр. следуетНекоторые приложенияКонцепция отличается, некоторые приложения должны «вырезать» многопараметрическую функцию, а каррирование — «вырезать» функцию много раз, пока каждая функция в середине не станет одним параметром, а конечным результатом будет так называемые функции Карри. Написать каррированную функцию на JS — это на самом деле написать функцию более высокого порядка, ничего особенного. Затем, чтобы реализовать общее карри, как это сделать, я ни на кого не ориентируюсь, я имею в виду, что две приведенные выше реализации все милые...

const curry = (fn) => {
    if (fn.length <= 1) return fn;
    
    // 这是我一开始的实现
    // 后来发现 rest 是多余的,下面这样就行了,fn.length 多处用到,可以提出来
    // const generator = (args) => (args.length === fn.length ? fn(...args) : arg => generator([...args, arg]));
    const generator = (args, rest) => (!rest ? fn(...args) : arg => generator([...args, arg], rest - 1));
    
    return generator([], fn.length);
};

Разве это не делается всего тремя строками кода (не считая объявлений функций), проверьте это:

const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum); 
const res = curriedSum(1)(2)(3) 
console.log(res); // 6

const log = (a, b, c) => {
    console.log(a, b, c);
};
const curriedLog = curry(log);
curriedLog('a')('b')('c'); // a b c

Кажется, что нет никаких проблем... эммммм... добро пожаловать в лицо.

Видно, что прежде чем метаться про карри и частичное применение, мне следовало бы больше подумать об основной концепции рекурсии, и кстати прикрепить быструю сортировку FP Style.Вдруг я чувствую, что тоже растрачиваю свою молодость...

const quickSort = (list) => {
    if (!list || !list.length) return [];
    if (list.length === 1) return list;

    const [middle, ...rest] = list;
    const reducer = (acc, x) => (
        x <= middle ? 
        { ...acc, left: [...acc.left, x] } : 
        { ...acc, right: [...acc.right, x] }
    );
    const { left, right } = rest.reduce(reducer, { left: [], right: [] });
    return [...quickSort(left), middle, ...quickSort(right)];
};

const list = [2, 3, 1, 8, 8, 1, 2, 18, 6, 2333];
const sorted = quickSort(list); // [ 1, 1, 2, 2, 3, 6, 8, 8, 18, 2333 ]

PS: Большой парень в комментариях дал однострочную реализацию:

const curry = (fn, arr = []) => (...args) => (
  arg => arg.length === fn.length
    ? fn(...arg)
    : curry(fn, arg)
)([...arr, ...args])

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

// 跟三行的思路是一样的,就是强行写到了一行……
const curry = fn =>
    (arg, args = [arg], rest = fn.length - 1) =>
    (rest < 1 ? fn(...args) : newArg => curry(fn)(newArg, [...args, newArg], rest - 1));

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

// 感觉还是多拆几行比较好……
const curry = fn =>
    (arg, args = [arg]) =>
    (!fn.length || args.length === fn.length ? fn(...args) : newArg => curry(fn)(newArg, [...args, newArg]));