将Javascript解决方案转换为函数式编程方法



我最近(非常)对函数式编程非常感兴趣,尤其是如何将其应用于JavaScript工作。在回答了一个关于regex使用的问题(此处链接)后,我继续进一步开发这些想法,目的是将其与函数式编程方法进行比较。

挑战在于编写一个简单的输入解析器,它接受正则表达式和一些输入,并返回一个匹配的对象数组(这是更大解决方案的第一步,但我想从简单开始)。我用的是更传统的方法,但我想用函数式编程做同样的事情(我使用的是ramda.js,但只要是JavaScript,我对任何函数式编程方法都持开放态度)。

这是工作代码:

var parseInput = function (re, input) {
var results = [], result;
while ((result = re.exec(input)) !== null) {
results.push({
startPos: result.index,
endPos: re.lastIndex - 1,
matchStr: result[1]
})
}
return results;
};
var re = /<%([^%>]+)%>/g;
var input = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD";
var results = parseInput(re, input);
console.log(results);

我得到的输出如下:

[ { startPos: 2, endPos: 15, matchStr: 'test.child' },
{ startPos: 16, endPos: 23, matchStr: 'more' },
{ startPos: 31, endPos: 38, matchStr: 'name' },
{ startPos: 45, endPos: 51, matchStr: 'age' } ]

这就是我正在寻找的结构和结果。

特别是,我一直在尝试Ramda和"match()"函数,但我看不到一种干净的方法来获取我要查找的对象数组(除了运行match(()来获取匹配数组,然后在原始输入中查找每个匹配数组之外,这似乎并不比我当前的解决方案麻烦)。

请提供指导。

Ramda的match对你没有帮助,这是对的。它是为更简单的用途而设计的。我看不出有什么比你的代码更好的了,尽管我可能会有不同的考虑:

const execAll = R.curry((re, convert, input) => {
let results = [], result;
while ((result = re.exec(input))) {
results.push(convert(result))
}
return results;
});
const parseInput = execAll(/<%([^%>]+)%>/g, match => ({
startPos: match.index,
endPos: match.index + match[0].length - 1,
matchStr: match[1]
}));
const input = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD";
parseInput(input);

显然,这段代码的结构有所不同,将regexexec调用的循环与输出的格式化分开。不过,更微妙的是,它也不依赖于正则表达式的全局状态,只使用返回的match结果中的信息作为输出。这对函数式编程来说非常重要。

对Ramda的curry的调用是纯粹的肉汁。你也可以把它写成

const execAll = (re, convert) => (input) => { /* ... */ }

如果您感兴趣,可以在Ramda REPL上找到。

请注意,这与您的方法没有太大变化。我看不出有什么明显不同的方法适用于一系列正则表达式。

只需稍微更改正则表达式,就可以使用String.prototype.match()方法执行以下操作;

var str = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD",
rex = /[^<%]+(?=%>)/g,
res = str.match(rex);
console.log(res);

好吧,如果你一直在应用正则表达式的这种严格的条件结构,那么你可能会考虑用一个非正则表达式函数代码以更快的方式完成同样的工作,如下所示;

var str = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD",
res = Array.prototype.reduce.call(str, function(r,c,i,s){
c === "%" && s[i-1] === "<" ? (r.select = true, r.push({startPos:i+1, endPos:undefined, matchStr: ""}))
   : c === ">" && s[i-1] === "%" ? (r.select = false, r[r.length-1].endPos = i-2)
                                 : r.select && c !== "%" && (r[r.length-1].matchStr.length ? r[r.length-1].matchStr += c
                                                                                           : r[r.length-1].matchStr  = c);
return r;
},[]);
console.log(res);

您会注意到起始位置和结束位置与您的示例不同,这只是因为它们给出了匹配子字符串的真实起始位置和终止位置。您可以很容易地更改代码,以包括<%%>的索引。

最新更新