2015-04-21 2 views
4

Я пишу файловый процессор в JavaScript (Node.js). Упрощенный псевдо-код, как показано ниже. Для каждой строки в файле она выводится на вывод, и если строка является индикатором включения, она рекурсивно обрабатывает включенный файл.JavaScript - рекурсивный вызов частичной прикладной функции

function fileProcessor (file) { 
    // read lines from file 
    lines.forEach(function (line) { 
    // #1 - include processor: 
    if line matches "#include includefile" 
     fileProcessor(includefile); 
    // #2 - output processor: 
    // write line to output 
    }); 
} 

Следующий шаг я хочу улучшить свой дизайн, отключив файловый процессор и линейный процессор. Я знаком с OO, он должен выглядеть как диаграмма классов. Но теперь я хочу попробовать это в FP.

Определить функцию, которая возвращает fileProcessor, которые применяются процессоры линии.

var fileProcessorGen = function (lineProcessors) { 
    return function (file) { 
    // read lines from file 
    lines.forEach(function (line) { 
     lineProcessors.forEach(function (processor) { 
     processor.call(this, line); 
     }); 
    }); 
    }; 
}; 

Моя цель - добиться разделения проблем. В этом примере есть только два линейных процессора, но на самом деле их будет больше. И вызывающий абонент будет использовать некоторые линейные процессоры в своей цели. Поэтому я не перекодирую их в файловый процессор или линейные процессоры.

var fileProcessor = fileProcessorGen([includeProcessor, outputProcessor]); 

Теперь моя проблема заключается в том, как я могу назвать fileProcessor (который является частичным приложением) внутри моего включенного процессора?

function includeProcessor(line) { 
    if line matches "#include includefile" 
    fileProcessor(includefile); // <--- how to get fileProcessor ? 
}; 

function outputProcessor(line) { 
    // write line to output 
} 

Возможно, мне нужно передать файлПроцессор в качестве аргумента в includeProcessor, но как?

function includeProcessor(line, fileProcessor) { 
    if line matches "#include includefile" 
    fileProcessor(includefile); 
}; 

var fileProcessorGen = function (lineProcessors) { 
    return function (file) { 
    // read lines from file 
    lines.forEach(function (line) { 
     lineProcessors.forEach(function (processor) { 
     processor.call(this, line, ???); // <-- how to pass the parameter? 
     }); 
    }); 
    }; 
}; 

ответ

1

Вы просто должны дать анонимную функцию имя, то вы можете обратиться к нему с помощью этого идентификатора внутри области видимости функции, независимо от того, что идентификатор, назначить эту функцию. (Я бы почти всегда рекомендовал давать имена функций, даже выражения функций, а не просто иметь возможность ссылаться на них, но облегчать чтение стеков стека).

function fileProcessorFactory (lineProcessors) { 
    return function fileProcessor (file) { 
    // read lines from file 
    lines.forEach(function (line) { 
     lineProcessors.forEach(function (processor) { 
     processor.call(this, line, fileProcessor); 
     }); 
    }); 
    }; 
}; 

var fileProcessorInstance = fileProcessorFactory([ includeProcessor, outputProcessor ]); 

Но вы, конечно, должны знать, что ваше текущее решение является крайне неэффективным, поскольку это создает совершенно новые наборы функций каждый раз, когда это называется, и вы могли бы сделать это намного проще и эффективнее, если бы вы просто объявить функции один раз и позвольте им называть друг друга.

function fileProcessor (file) { 
    //split those lines 
    lines.forEach(lineProcessor); 
} 

function lineProcessor (line) { 
    if line matches "#include includefile" 
     fileProcessor(includefile); 
    else 
     outputProcessor(line); 
} 

function outputProcessor (line) { 
    //... 
} 

Если вы хотите поддерживать несколько директив, просто искать их по имени в HashMap ака «в JavaScript Object» или выдаст ошибку, если имя не в карте. (есть также фактический тип данных Map в ES6, который вы также можете использовать для этого).

var directives = { 
    include : function includeDirective (expression) { 
    }, 
    define : function defineDirective (expression) { 
    }, 
    //... 
}; 

Вы также можете сделать это совершенно без регулярных выражений, потому что препроцессор обычно только следовать 3 супер простых правил (в псевдокоде)

  1. если первая буква «#» = это директива
  2. , а не пространство = имя директивы
  3. , а не новая строка = выражение

Для уточнения п проблема создания связи. Функциональный характер javascript разделяет функции на объявления функций и выражения функций.Объявления функций оцениваются один раз, выражения функций оцениваются каждый раз, когда элемент управления достигает строки, содержащей его, что приводит к новой функции, которую вы также можете хранить в переменной или нет. Если вы его не храните, он становится мусором (например, после функции, которую вы передали ей для возврата).

lines.forEach(function (line) { // creates one new function 
    lineProcessors.forEach(function (processor) { //creates one new function per line 
    processor.call(this, line); 
    });// one function per line is now garbage 
}); // the outer function is now garbage 
+0

Моя цель - добиться разделения проблем. В этом примере есть только два линейных процессора, но на самом деле их будет больше. И вызывающий абонент будет использовать некоторые линейные процессоры в своей цели. – aleung

+0

BTW, интересно, в моем решении функции линейного процессора действительно созданы каждый раз, когда он называется? Я думаю, что функции создаются один раз путем объявления, а ссылки передаются в массиве. Я новичок в JavaScript, пожалуйста, исправьте меня, если я ошибаюсь. – aleung

+0

@aleung Надеюсь, я мог бы это немного разъяснить. Вы не одиноки с этой путаницей. Люди часто начинают искать самые причудливые функции, а затем через некоторое время возвращаются к использованию самых простых функций, потому что их легко писать/читать/поддерживать/не испортить и обычно выполнять намного лучше :) – Winchestro

0

Вы можете создать новый экземпляр fileProcessor, а затем использовать его внутри includeProcessor.

function includeProcessor(line) { 
    if line matches "#include includefile" 
    { 
    var fp = fileProcessorGen([includeProcessor, outputProcessor]); 
    fp(includefile); 
    } 
}; 
+0

Да, это работает. Но моя цель - добиться разделения проблем. Include Processor не должен знать, какие другие процессоры используются. – aleung

Смежные вопросы