Вызов и передача вложенных функций в JavaScript - PullRequest
0 голосов
/ 15 ноября 2018

У меня есть функция, которая возвращает LCM диапазона чисел.Он прекрасно работает, но у него есть функция внутри функции внутри функции.У меня вопрос, почему я не могу упростить вложенный smalllestCommon (), удалив из него scm ()?Почему это конкретное решение нуждается в этом, если еще функциональность так глубоко вложена?

function smallestCommons(arr) {
  var max = Math.max(...arr);
  var min = Math.min(...arr);
  var candidate = max;

  var smallestCommon = function(low, high) {
  // inner function to use 'high' variable
    function scm(l, h) {
      if (h % l === 0) {
         return h;
      } else {
        return scm(l, h + high);
      }
    }
    return scm(low, high);
  };

  for (var i = min; i <= max; i += 1) {
    candidate = smallestCommon(i, candidate);
  }

  return candidate;
}

smallestCommons([5, 1]); // should return 60
smallestCommons([1, 13]); // should return 360360
smallestCommons([23, 18]); //should return 6056820

Ответы [ 3 ]

0 голосов
/ 15 ноября 2018

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

В вашем конкретном случае, однако, нет необходимости иметь его вложенным.Вы можете просто передать переменную high в качестве третьего параметра:

function scm(l, h, high) {
  if (h % l === 0) {
     return h;
  } else {
    return scm(l, h + high, high);
  }
}

function smallestCommon (low, high) {
  return scm(low, high, high);
}

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


И этоЖаль, что JS не имеет range функции .smallestCommons - это просто сокращение диапазона [min,max].Однако из-за отсутствия функции range и smallestCommon с аргументами в неправильном порядке преобразование вашего кода в reduce, к сожалению, получилось немного громоздким:

function smallestCommons(arr) {
  var max = Math.max(...arr);
  var min = Math.min(...arr);

  return Array.from(new Array(max - min), (x,i) => i + min)
              .reduce((acc, i) => smallestCommon(i, acc), max);
}
0 голосов
/ 15 ноября 2018

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

const gcd = (m, n) =>
  n === 0
    ? m
    : gcd (n, m % n)

const lcm = (m, n) =>
  Math.abs (m * n) / gcd (m, n)
  
console.log
  ( lcm (1, 5)    // 5
  , lcm (3, 4)    // 12
  , lcm (23, 18)  // 414
  )

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

const None =
  Symbol ()

const list = (...values) =>
  values

const minmax = ([ x = None, ...rest ], then = list) =>
  x === None
    ? then (Infinity, -Infinity)
    : minmax
        ( rest
        , (min, max) =>
            then
              ( Math.min (min, x)
              , Math.max (max, x)
              )
        )

console.log
  ( minmax ([ 3, 4, 2, 5, 1 ])    // [ 1, 5 ]
  , minmax ([ 1, 5 ])             // [ 1, 5 ]
  , minmax ([ 5, 1 ])             // [ 1, 5 ]
  , minmax ([ 9 ])                // [ 9, 9 ]
  , minmax ([])                   // [ Infinity, -Infinity ]
  )

По умолчанию minmax возвращает list изминимальное и максимальное значения.Мы можем подключить min и max напрямую к функции range, которая может быть более полезной для нас, как мы увидим позже -

const range = (m, n) =>
  m > n
    ? []
    : [ m, ... range (m + 1, n ) ]

console.log
  ( minmax ([ 3, 4, 2, 5, 1 ], range)    // [ 1, 2, 3, 4, 5 ]
  , minmax ([ 1, 5 ], range)             // [ 1, 2, 3, 4, 5 ]
  , minmax ([ 5, 1 ], range)             // [ 1, 2, 3, 4, 5 ]
  , minmax ([ 9 ], range)                // [ 9 ]
  , minmax ([], range)                   // []
  )

Теперь, когда мы можем найти min и max длявход, создать диапазон между двумя, все, что осталось, это вычисление lcm значений в диапазоне.Взятие множества значений и приведение их к одному значению выполняется с помощью .reduce -

console.log
  ( minmax ([1, 5], range) .reduce (lcm, 1) // 60
  , minmax ([5, 1], range) .reduce (lcm, 1) // 60
  )

Оберните это в функцию, и все готово -

const smallestCommons = xs =>
  minmax (xs, range) .reduce (lcm, 1)

console.log
  ( smallestCommons ([ 5, 1 ])    // 60
  , smallestCommons ([ 1, 13 ])   // 360360
  , smallestCommons ([ 23, 18 ])  // 6056820
  )

Проверьте результат в вашем собственном браузере ниже -

const gcd = (m, n) =>
  n === 0
    ? m
    : gcd (n, m % n)

const lcm = (m, n) =>
  Math.abs (m * n) / gcd (m, n)

const None =
  Symbol ()

const list = (...values) =>
  values

const minmax = ([ x = None, ...xs ], then = list) =>
  x === None
    ? then (Infinity, -Infinity)
    : minmax
        ( xs
        , (min, max) =>
            then
              ( Math.min (min, x)
              , Math.max (max, x)
              )
        )

const range = (m, n) =>
  m > n
    ? []
    : [ m, ... range (m + 1, n ) ]

const smallestCommons = xs =>
  minmax (xs, range) .reduce (lcm, 1)

console.log
  ( smallestCommons ([ 5, 1 ])    // 60
  , smallestCommons ([ 1, 13 ])   // 360360
  , smallestCommons ([ 23, 18 ])  // 6056820
  )

extra

Выше minmax определяется с использованием стиля передачи продолжения.Мы сохраняем дополнительные вычисления, передавая range в качестве указанного продолжения (then).Однако мы можем вызвать minmax без указания продолжения и распространить (...) промежуточное значение до range.Любая программа может иметь больше смысла для вас.Результат тот же -

const smallestCommons = xs =>
  range (...minmax (xs)) .reduce (lcm, 1)

console.log
  ( smallestCommons ([ 5, 1 ])    // 60
  , smallestCommons ([ 1, 13 ])   // 360360
  , smallestCommons ([ 23, 18 ])  // 6056820
  )

та же свинья, другая ферма

smallestCommons - это просто сокращение диапазона [min,max] - @ Carcigenicate

Надеюсь, это помогает увидеть один и тот же результат с нескольких подходов: D


Sourface

Некоторые люди презирают приведенную выше реализацию minmax независимо от ее элегантности и гибкости.Теперь, когда мы немного лучше понимаем сокращение, мы можем показать, как лучше реализовать minmax с помощью прямого стиля -

const minmax = xs =>
  xs .reduce
    ( ([ min, max ], x) =>
        [ Math.min (min, x)
        , Math.max (max, x)
        ]
    , [ Infinity, -Infinity ]
    )

const smallestCommons = xs =>
  range (...minmax (xs)) .reduce (lcm, 1) // direct style now required here
0 голосов
/ 15 ноября 2018

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

function scm(l, h, step) {
  if (h % l === 0) {
     return h;
  } else {
    return scm(l, h + h, step);
  }
}

function smallestCommons(arr) {
  var max = Math.max(...arr);
  var min = Math.min(...arr);
  return scm(min, max, max);
}

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

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