我(非常)最近对函数式编程非常感兴趣,特别是如何将它应用于我在JavaScript中的工作。在回答有关正则表达式使用的问题后(链接here),我继续开发这些想法,目的是将其用于与函数式编程方法进行比较。
挑战是编写一个简单的输入解析器,它接受一个正则表达式和一些输入并返回一个匹配的对象数组(这是更大的解决方案的第1步,但我想开始简单)。我让它使用更传统的方法,但是希望与函数式编程相同(我使用的是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()来获取一系列匹配,然后在原始输入中查找每个匹配,这似乎不比我当前的解决方案麻烦。)
指导将不胜感激。
答案 0 :(得分:1)
你是对的,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);
显然,这段代码的结构不同,打破了正则表达式exec
调用与输出格式的循环。但更巧妙的是,它也不依赖于正则表达式的全局状态,仅使用返回的match
结果中的信息作为其输出。这对于函数式编程来说非常重要。
对Ramda的curry
的呼唤是纯粹的肉汁。您也可以将其写为
const execAll = (re, convert) => (input) => { /* ... */ }
如果您有兴趣,可以在 Ramda REPL 上找到。
请注意,这与您的方法相比没有太大变化。我没有看到适用于一系列正则表达式的显着不同的方法。
答案 1 :(得分:0)
使用String.prototype.match()
方法稍微改变您的正则表达式,您可以按照以下步骤操作;
var str = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD",
rex = /[^<%]+(?=%>)/g,
res = str.match(rex);
console.log(res);
&#13;
如果您将一直使用这种严格的正则表达式条件结构,那么您可能会认为非正则函数代码以更快的方式执行相同的工作,如下所示;
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);
&#13;
您会注意到开始和结束位置与您的示例不同,这只是因为它们给出了匹配的子字符串的真实开始和结束位置。您可以轻松更改代码以包含<%
和%>
的索引。