Рекурсия изнутри IIFE - PullRequest
       28

Рекурсия изнутри IIFE

0 голосов
/ 04 сентября 2018

У меня есть фрагмент кода, который генерирует все возможные строки, которые можно создать, помещая пробелы между буквами данной строки, код использует рекурсию для достижения этой цели. Вот код, который у меня есть (это C ++ source Я адаптировался к JavaScript, и он работает как положено):

    var genStringsUtil = function (str,buf,i,j,n){
      if(n == i){
        buf[j] = " ";
        console.log(buf.join(""));
        return;
      }
        buf[j] = str[i];
        genStringsUtil (str,buf,i+1,j+1,n);
        buf[j] = " ";
        buf[j+1] = str[i];
        genStringsUtil (str,buf,i+1,j+2,n);
    }

   var genStrings = function(s){
      var str = s;
      var n =str.length;
      var buf = [];
      buf[0] = str[0];

      genStringsUtil (str,buf,1,1,n);
    };

   function main(){
    genStrings("ABCDE");
   }

   main();

Теперь я изменил его так, и он все еще работает:

var genStrings = function (str,buf,i,j,n){
  if(n == i){
    buf[j] = " ";
    console.log(buf.join(""));
    return;
  }
  buf[j] = str[i];
  genStrings (str,buf,i+1,j+1,n);
  buf[j] = " ";
  buf[j+1] = str[i];
  genStrings (str,buf,i+1,j+2,n);
}

!function(s){
  var str = s;
  var n =str.length;
  var buf = [];
  buf[0] = str[0];

  genStrings (str,buf,1,1,n);
}("ABCDE");

Однако, когда я изменяю последнюю часть на (IIFE с круглыми скобками):

(function(s){
  var str = s;
  var n =str.length;
  var buf = [];
  buf[0] = str[0];

  genStrings (str,buf,1,1,n);
})("ABCDE");

Я получаю сообщение об ошибке:

Ошибка типа: j не определена

Если я поставлю закрывающую скобку после ("ABCDE") примерно так:

(function(s){
  var str = s;
  var n =str.length;
  var buf = [];
  buf[0] = str[0];

  genStrings (str,buf,1,1,n);
}("ABCDE"));

Я получаю еще одну ошибку:

TypeError: genStrings не является функцией

Я всегда думал, что IIFEs объявлены с! или круглые скобки - то же самое, но, очевидно, нет. Итак, мой вопрос, в основном, что происходит, что отличается в этих трех случаях? Является ли рекурсия проблемой?

Надеюсь, мое сообщение не слишком длинное.

Спасибо за вашу помощь.

1 Ответ

0 голосов
/ 04 сентября 2018

C ++ имеет несколько ограничений, которых нет в JavaScript. Существует бесчисленное множество способов кодирования вашей функции в JavaScript, но вот тот, который использует стиль передачи с продолжением .

В этом стиле второй параметр send добавляется к подписи функции genStrings и принимает продолжение по умолчанию identity. Это эффективно превращает return в настраиваемую пользователем функцию. Имя send используется, потому что return является зарезервированным ключевым словом.

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

const identity = x =>
  x
  
const concat = (xs, ys) =>
  xs .concat (ys)
  
const genStrings = ([ char, ...rest ], send = identity) =>
  // base case: return empty set
  char === undefined
    ? send ([])

  // if there is only one char, return singleton result
  : rest.length === 0
    ? send ([char])
  
  // otherwise recur on rest
  // 1) add char plus space to each combination
  // 2) add char without space to each combination
  // 3) concat the result
  : genStrings
      ( rest
      , combs =>
          send ( concat ( combs .map (c => char + ' ' + c)
                        , combs .map (c => char + c)
                        )
               )
      )

console.log (genStrings ('ABC'))
// [ 'A B C'
// , 'A BC'
// , 'AB C'
// , 'ABC'
// ]

Вы также заметите, что нет необходимости отслеживать несколько других переменных состояния, таких как n, i и j, и не нужно увеличивать их с другими значениями. Программы, использующие меньше выражений и переменных, легче поддерживать и легче отлаживать.

genStrings работает и с более крупными входами, как пример в вашем вопросе

console.log (genStrings ('ABCDE'))
// [ 'A B C D E'
// , 'A B C DE'
// , 'A B CD E'
// , 'A B CDE'
// , 'A BC D E'
// , 'A BC DE'
// , 'A BCD E'
// , 'A BCDE'
// , 'AB C D E'
// , 'AB C DE'
// , 'AB CD E'
// , 'AB CDE'
// , 'ABC D E'
// , 'ABC DE'
// , 'ABCD E'
// , 'ABCDE'
// ]

Мы также осторожно сделали genStrings итоговую программу , что означает, что она возвращает действительный результат, даже если входная строка пуста

console.log (genStrings (''))
// []

Поскольку genStrings определяется с использованием стиля передачи продолжения, мы также можем указать настраиваемое пользователем продолжение на сайте вызова

genStrings ('ABC', console.log)
// [ 'A B C', 'A BC', 'AB C', 'ABC' ]

genStrings ('ABC', combs => combs.length)
// 4

genStrings ('ABC', combs => combs .join (', '))
// 'A B C, A BC, AB C, ABC'

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


Я начал изучать его, но не могу понять, что такое синтаксис двоеточия перед rest.length === 0 и genStrings?

?: - это условный оператор JavaScript , также называемый троичным оператором . Синтаксис conditionExpression ? trueExpression : falseExpression. Когда conditionExpression оценивается как истинное значение, оценивается только trueExpression, и falseExpression пропускается. И наоборот, если conditionExpression оценивается как неверное значение, trueExpression пропускается и оценивается только falseExpression.

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

// conditional expression
let someValue =
  n === 0                // expression
    ? "n is zero"        // expression
    : "n is not zero"    // expression

// if statement
let someValue
if (n === 0)
  someValue = "n is zero"         // side effect
else
  someValue = "n is not zero"     // side effect

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

Аналогично операторам if - else if - else, условные выражения также могут быть объединены в цепочку. Это синтаксис, который вы видите в ответе выше.

const animalSound =
  animal === "dog"    // if
    ? "woof"          // then

  : animal === "cat"  // else if
    ? "meow"          // then

  : "unknown"         // else

Это помогает мне увидеть одну и ту же программу, представленную различными способами. Ниже мы переписываем исходный ответ, используя выражения императивного стиля if

const identity = x =>
  x

const concat = (xs, ys) =>
  xs .concat (ys)

const genStrings = ([ char, ...rest ], send = identity) =>
{ if (char === undefined)
    return send ([])

  else if (rest.length === 0)
    return send ([char])

  else
    return genStrings
             ( rest
             , combs =>
                 send ( concat ( combs .map (c => char + ' ' + c)
                               , combs .map (c => char + c)
                               )
                      )
             )
}

console.log (genStrings ('ABC'))
// [ 'A B C'
// , 'A BC'
// , 'AB C'
// , 'ABC'
// ]
...