我有一段代码生成所有可能的字符串,这些字符串可以通过在给定字符串的字母之间放置空格来生成,该代码使用递归来实现。这是我的代码(这是我适应了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声明为!或括号是同一回事,但显然不是。所以我的问题是,这三种情况基本上是怎么回事?递归是问题吗?
希望我的信息不要太长。
谢谢您的帮助。
答案 0 :(得分: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'
// ]
您还将注意到,无需跟踪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
设为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
是一个具有明确定义的域(输入)和共域(输出)的纯函数,因此不需要立即调用的函数表达式(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'
// ]