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

Вы правы, что match Ramda не поможет. Он предназначен для более простого использования. Я не вижу ничего существенно лучше, чем ваш код, хотя я мог бы фактор по-другому:

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 для своего вывода. Это поражает меня как важное для функционального программирования.

Призыв к Рамде 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() - функцией в Рамде, используя мое текущее регулярное выражение, но не массив объектов. Я ищу ответ, применяющий концепции функционального программирования, чтобы расширить мое понимание. – rasmeister

+0

@rasmeister OK Я включил код в свой ответ, который даст вам объекты, которые вы ищете, без использования регулярного выражения. – Redu