简体   繁体   English

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

[英]Converting Javascript solution to Functional Programming Approach

I have (very) recently gotten quite interested in functional programming, and in particular, how to apply this to my work in JavaScript. 最近,我(非常)对函数式编程非常感兴趣,尤其是如何将其应用于我的JavaScript工作。 After answering a question regarding regex use (link here ), I continued to develop the ideas a bit more with the intent of using this to compare with a functional programming approach. 在回答了有关正则表达式使用的问题之后(我在这里 ,链接),我继续进行更多的构想,以将其与功能性编程方法进行比较。

The challenge is to write a simple input parser that takes a regex and some input and returns a matching array of objects (this is step 1 of the bigger solution, but I wanted to start simple). 面临的挑战是编写一个简单的输入解析器,该解析器接受一个正则表达式和一些输入并返回匹配的对象数组(这是更大解决方案的步骤1,但我想从简单开始)。 I have it working with the more traditional approach, but would like to do the equivalent with functional programming (I am using ramda.js, but open to any functional programming approach as long as it is in JavaScript). 我可以使用更传统的方法来使用它,但是想对功能编程做同样的事情(我使用的是ramda.js,但只要是JavaScript,就可以使用任何功能编程方法)。

Here's the working code: 这是工作代码:

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);

The output I get looks like this: 我得到的输出看起来像这样:

[ { 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' } ]

which is the structure and results I am looking for. 这是我要寻找的结构和结果。

In particular, I have been experimenting with Ramda and the 'match()' function, but I can't see a clean way to get the array of objects I am looking for (short of running the match() to get an array of matches and then looking each one up in the original input which seems no less cumbersome than my current solution). 特别是,我一直在尝试使用Ramda和'match()'函数,但是我看不到一种干净的方法来获取想要的对象数组(缺少运行match()来获取数组)的方法。匹配项,然后在原始输入中查找每个匹配项,这似乎比我当前的解决方案麻烦得多)。

Guidance would be appreciated. 指导将不胜感激。

You're right that Ramda's match will not help you. 没错,Ramda的match不会为您提供帮助。 It's designed for simpler uses. 专为更简单的用途而设计。 I don't see anything substantially better than your code, although I might factor it differently: 我认为没有什么比您的代码更好的了,尽管我可能会有所不同:

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);

Obviously, this code is structured differently, breaking apart the looping of the regex exec calls from the formatting of the output. 显然,此代码的结构不同,将regex exec调用的循环与输出格式分开。 More subtly, though, it also does not rely on the global state of the regex, using only information from the returned match results for its output. 不过,更巧妙的是,它也不依赖于正则表达式的全局状态,仅将返回的match结果中的信息用于其输出。 That strikes me as important for functional programming. 这对我来说对函数式编程很重要。

The call to Ramda's curry is pure gravy. Ramda curry招牌是纯肉汁。 You could also write this as 您也可以这样写

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

This is available on the Ramda REPL , if you're interested. 如果您有兴趣,可以在Ramda REPL上使用。

Do note that this is not much changed from your approach, though. 请注意,这与您的方法相比并没有太大变化。 I don't see a significantly different approach applicable across a range of regular expressions. 我没有看到适用于一系列正则表达式的明显不同的方法。

Just slightly altering your regex you might do as follows by using the String.prototype.match() method; 只需稍微更改您的正则表达式,就可以使用String.prototype.match()方法执行以下操作:

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

Well in case you will have this strict conditional structure of regex applying all the time then you might consider a non regex functional code doing the same job in a much faster manner as follows; 好吧,如果您一直使用这种严格的regex条件结构,那么您可以考虑使用非regex功能代码以更快的方式完成相同的工作,如下所示;

 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); 

You will notice that the start and end positions differ from your example and that's just because they are giving the true start and end positions of the matched sub strings. 您会注意到开始位置和结束位置与您的示例有所不同,这仅仅是因为它们给出了匹配子字符串的真实开始位置和结束位置。 You may easily alter the code to include the indices of <% and %> as well. 您可以轻松地更改代码以包括<%%>的索引。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM