Рефакторинг от процедурной парадигмы к функциональной парадигме

В процессе изучения функционального программирования я пытаюсь реорганизовать следующий код, используя карту, фильтр и/или сокращение.

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

Я думаю, что я бы использовал метод карты для обработки назначений diff и связал бы метод фильтра, который будет иметь дело с условным. Я на правильном пути?

Кто-нибудь будет достаточно любезен, чтобы реорганизовать следующий код в функциональной парадигме и объяснить. Спасибо.

Эта функция находит первое непоследовательное число в массиве.

 function firstNonConsecutive (arr) {
   var diff = 0;
    for(var i = 0; i < arr.length; i++) {
        diff = arr[i+1] - arr[i];
        if(diff > 1) {
            return arr[i+1];
        }
    }
    return null;

person efw    schedule 05.06.2020    source источник
comment
Повторное задание довольно бессмысленно. Код работает так же, как если бы diff был объявлен с const внутри тела цикла.   -  person Bergi    schedule 06.06.2020


Ответы (4)


Рассмотрите возможность использования Array.find https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find, как и большинство функциональных функций массива, он принимает обратный вызов/предикат, который принимает 3 параметра: элемент, индекс и весь массив. При этом вы можете смотреть вперед/назад так же, как вы это делаете сейчас. Как это:

 function firstNonConsecutive2(arr) {
   return arr.find((item, index, array) => {
     const diff = array[index - 1] - item; // Use index -1 to look behind instead of ahead since we want to return/find the item that is non-consecutive
     return diff === 0; // I think this was a small bug in your version which worked if the numbers were incrementing only
   });
 }

В первой итерации «цикла» поиска он попытается сравнить undefined, например, с 1, что равно NaN, NaN не равно 0, поэтому он продолжит поиск. Затем он попытается, возможно, 1 и 2, так что diff станет -1, поэтому он продолжит поиск. Пока он не достигнет, например, 5 и 5, которые отличаются от 0, поэтому предикат find теперь истинен, поэтому он вернет второй 5, поскольку это текущий элемент, мы оглядываемся назад, используя index - 1.

Дайте мне знать, если вам нужно дальнейшее объяснение чего-либо!

person Milton    schedule 05.06.2020
comment
Спасибо, Милтон. Это полезно. Алгоритм, который я написал, был предназначен для вызова, в котором говорилось, что все числа уникальны и расположены в порядке возрастания, что может объяснить то, что рассматривается как ошибка. Функциональное программирование для меня новое, поэтому мне придется повнимательнее изучить то, что вы написали. Еще раз спасибо, что нашли время и усилия, чтобы помочь. - person efw; 05.06.2020

Если вы изучаете fp, то и здесь рекурсия найдет хорошее применение:

const firstNonConsecutive = (list) => {
  if (!list.length) { 
    // list is empty, not found
    return -1;
  }
  
  const [head, ...tail] = list;
  const [n] = tail;
  
  if (n - 1 !== head) { 
    // found
    return n;
  }
  
  // yet another round
  return firstNonConsecutive(tail);
};

console.log(
  firstNonConsecutive([1, 2, 3, 4, 5, 6, 7, 9, 10]),
);

person Hitmands    schedule 06.06.2020

Вы хотите использовать Array.find, а не .map, .filter или .reduce. Вы могли бы найти способ их использовать, но они тратят время впустую, потому что они не возвращаются, как только будет найдено первое совпадение.

Вот несколько более подробных решений, которые помогут вам понять, как работает первое.

Второй наиболее функциональный, потому что он декларативен в отличие от первого.

array.find(nonConsecutive) читается близко к простому английскому языку и декларирует то, что вы хотите сделать, оставляя императивные детали реализации скрытыми внутри функции nonConsecutive.

const array = [1, 2, 3, 4, 5, 6, 7, 9, 10];

console.log(
  array.find((n, i) => i && n != array[i - 1] + 1) // 9
);

const nonConsecutive = (n, i, arr) => i && n != arr[i - 1] + 1;
console.log(
  array.find(nonConsecutive) // 9
);

console.log(
  array.find(function (number, index) { // we don't need third "array" argument because the array is already in scope.
    if (index == 0) return false; // if index is zero, return. Otherwise, next line would access index -1.
    if (number != array[index - 1] + 1) return true; // if number is not equal to the the previous number, plus one, it's not consecutive. Return it.
    return false; // if we reach this line than the number must be consecutive, so return false.
  }) // 9
);

person GirkovArpa    schedule 08.08.2020

Рекурсия по математической индукции -

  1. Если первый элемент, a, или второй элемент, b, равны нулю, вернуть undefined
  2. (индукция) Ни первый элемент a, ни второй элемент b не равны нулю. Если b следует за a, вернуть рекурсивный результат меньшей задачи.
  3. (индукция) Ни первый элемент, a, ни второй элемент, b, не являются нулевыми, а b не следует за a. Верните ответ, b.

const firstNonConsecutive = ([ a, b, ...more ]) =>
  a == null || b == null
    ? undefined                            // 1
: b === a + 1
    ? firstNonConsecutive([ b, ...more ])  // 2
: b                                        // 3

console.log(firstNonConsecutive([ 4, 5, 6, 8, 9, 10 ]))
// 8

console.log(firstNonConsecutive([ 7, 8, 9, 10, 13, 14 ]))
// 13

console.log(firstNonConsecutive([ 99 ]))
// undefined

console.log(firstNonConsecutive([]))
// undefined

person Mulan    schedule 09.08.2020