来自IIFE内部的递归

问题描述 投票:0回答:1

我有一段代码生成所有可能的字符串,可以通过在给定字符串的字母之间放置空格来生成,代码使用递归来实现这一点。这是我的代码(它是我已经适应JavaScript的C++ source,它按预期工作):

    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");

我收到一个错误:

TypeError: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不是一个函数

我一直认为IIFE声明了!或括号是相同的,但显然不是。所以我的问题基本上是在这三种情况下发生了什么不同?递归问题是什么?

我希望我的信息不会太久。

谢谢您的帮助。

javascript recursion iife
1个回答
0
投票

C ++有一些JavaScript没有的限制。有许多方法可以在JavaScript中对函数进行编码,但这里有一个使用continuation-passing style的方法。

在这种风格中,第二个参数send被添加到你的genStrings函数的签名中,并接受默认的identity延续。这有效地将return变成了用户可配置的功能。使用名称send是因为return是保留关键字。

此实现的另一个值得注意的方面是使用referential transparency,这是一种功能样式的属性,其中函数总是为相同的输入返回相同的结果。由于递归是一种功能性遗产,我们一定会保持这种特性以产生最佳结果。

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'
// ]

您还会注意到,不需要跟踪其他几个状态变量,如nij,也不需要使用不同的值来增加它们。使用较少表达式和变量的程序更易于维护且更易于调试。

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成为total program,这意味着它返回一个有效的结果,即使输入字符串是空的

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是一个具有良好定义的域(输入)和codomain(输出)的纯函数,因此不需要立即调用的函数表达式(IIFE),更不用说需要调试一个。


我开始研究它,但我无法理解什么是:rest.length === 0和genStrings之前的冒号语法?

?:是JavaScript的conditional operator,也称为三元运算符。语法是conditionExpression ? trueExpression : falseExpression。当conditionExpression评估为truthy值时,仅评估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'
// ]
© www.soinside.com 2019 - 2024. All rights reserved.