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

Converting Javascript solution to Functional Programming Approach

我最近(非常)对函数式编程非常感兴趣,尤其是如何将其应用到我在 JavaScript 的工作中。在回答了一个关于正则表达式使用的问题 (link ) 之后,我继续进一步发展这些想法,目的是将其与函数式编程方法进行比较。

挑战在于编写一个简单的输入解析器,该解析器采用正则表达式和一些输入以及 returns 匹配的对象数组(这是更大解决方案的第 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() 获取一组匹配项,然后在原始输入中查找每个匹配项,这似乎不比我当前的解决方案麻烦。

不胜感激。

只需稍微改变您的正则表达式,您就可以使用 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);

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

你是对的,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 上找到。

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