2017-01-22 97 views
0

我已(非常)近日得到了函数式编程很感兴趣,尤其是,如何将这些应用到我在JavaScript的工作。回答关于正则表达式使用的问题后(​​链接here),我继续发展这些想法,目的是使用它与功能性编程方法进行比较。转换JavaScript解决方案,以函数式编程方法

的挑战是如何写一个简单的输入解析器,需要一个正则表达式和一些输入并返回对象的匹配阵列(这是更大的溶液的步骤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()得到一串匹配,然后在原始输入中查看每一个,看起来不亚于我当前的解决方案)。

指导,将不胜感激。

回答

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,如果你有兴趣。

请注意,虽然这与您的方法没有太大的改变。我没有看到适用于各种正则表达式的显着不同的方法。

+0

谢谢斯科特。这正是我所期待的!我问了这个问题,因为我看不到一种方法去除对正则表达式状态的依赖。你的解决方案提供了这个我喜欢数据格式的分离,对咖喱的评论也很有帮助。 – rasmeister

0

只要稍微改变你的正则表达式,你可以通过使用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);

你会发现,在开始和结束位置从你的例子不同,这只是因为他们给匹配的子串的真正开始和结束位置。您可以轻松更改代码以包含<%%>的索引。

+0

此解决方案没有我正在寻找的开始和结束位置的对象。我可以很容易地用R.match() - 一个使用我当前正则表达式的Ramda中的函数,而不是对象数组获得同样的结果。我正在寻找应用函数式编程概念的答案,以扩大我的理解。 – rasmeister

+0

@rasmeister好的我已经包含了一个代码给我的答案,这会给你你正在寻找的对象,而不使用正则表达式。 – Redu