将Javascript解决方案转换为功能编程方法

时间:2017-01-22 17:26:57

标签: javascript regex ramda.js

我(非常)最近对函数式编程非常感兴趣,特别是如何将它应用于我在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()来获取一系列匹配,然后在原始输入中查找每个匹配,这似乎不比我当前的解决方案麻烦。)

指导将不胜感激。

2 个答案:

答案 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()方法稍微改变您的正则表达式,您可以按照以下步骤操作;

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

如果您将一直使用这种严格的正则表达式条件结构,那么您可能会认为非正则函数代码以更快的方式执行相同的工作,如下所示;

&#13;
&#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;
&#13;
&#13;

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