2017-01-22 95 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