在编程风格课程中,我们要求以“连续传递风格”和“糖果工厂风格”两种方式实现一些代码。
我们正在阅读的书是克里斯蒂娜·维迪拉·洛佩斯(Cristina Videira Lopes)的“编程风格的练习”(第5章和第8章)。
我们被要求用另一种语言来实现本书的示例代码(本书使用的是Python,现在我正在使用Javascript)。
为了理解我的问题,我将向您展示书中显示的两个主要区别:
糖果工厂风格
#!/usr/bin/env python
read_file(path_to_file):
"""
Takes a path to a file and returns the entire contents of the
file as a string
"""
with open(path_to_file) as f:
data = f.read()
return data
def filter_chars_and_normalize(str_data):
...
.
.
.
print_all(sort(frequencies(remove_stop_words(scan(
filter_chars_and_normalize(read_file(sys.argv[1]))))))[0:25])
连续传球风格
#!/usr/bin/env python
def read_file(path_to_file, func):
with open(path_to_file) as f:
data = f.read()
func(data, normalize)
def filter_chars(str_data, func):
pattern = re.compile(’[\W_]+’)
func(pattern.sub(’ ’, str_data), scan)
.
.
.
read_file(sys.argv[1], filter_chars)
Java脚本
const fs = require('fs');
var myArgs = process.argv.slice(2);
function read_file(path_to_file,callback){
fs.readFile(path_to_file, "utf-8",(err, data) => {
if (err) throw err;
callback(data);
});
}
function string_to_lower(str_data,callback){
var data = str_data.toLowerCase()
callback(data)
}
.
.
.
function TermFrequency(){
read_file(myArgs[0],function(result){
string_to_lower(result, function(result2){
remove_non_alphanumeric(result2,function(result3){
remove_stop_words(result3,function(result4){
frequencies(result4,function(result5,result6){
sort(result5,result6,function(result7,result8){
write_out(result7,result8)
})
})
})
})
})
})
}
从我的理解以及本书的示例中,上面用Javascript编写的内容都是Continuation传递,因为函数是作为参数传递的。但是同时,为了调用主函数,您需要使用与Candy Factory相同的“管道风格”调用。
给定上面用JS编写的代码,如何实现糖果工厂风格?该代码(基于回调)是糖果工厂还是连续传递样式?如何在不使用回调并同时信任JS的情况下编写以上代码?
最佳答案
我认为您在混淆两件事。糖果风格通常被称为功能组合。那是一个功能的输出是下一个功能的输入的地方。
f(g(h(1)))
h(1)
输出一个值,它是g
的输入,它输出一个值,并且是f
的输入。这与Javascript中用于异步操作的回调样式不同。
f(1,g)
f
取值,进行处理并在以后调用g
的地方。通常,在JavaScript中,您将需要处理异步操作,但在这种情况下,您仅需要回调(继续)。像
stringToLower
这样的函数仅需要返回数据。function string_to_lower (str) {
return str.toLowerCase();
}
如果您要调整代码以遵循这些规则,那么您可以做一些更熟悉的事情:
function TermFrequency(){
read_file(myArgs[0],function(result){
write_out( sort(frequencies(remove_stop_words(remove_non_alphanumeric(string_to_lower(result))))));
}
}
知道这是组成,我们可以使用另一个函数来进一步简化它。
function compose (...fns) {
return function (value) {
fns.reduce(function (result, fn) {
return fn(result);
}, value);
}
}
我们可以这样使用它:
const processFile = compose(
string_to_lower,
remove_non_alphanumeric,
remove_stop_words,
frequencies,
sort,
write_out,
);
function TermFrequency(){
read_file(myArgs[0], processFile);
}
现在,这看起来很陌生,但让我们逐步了解一下。该函数compose接受称为
fns
的参数列表。 ...
(其余运算符)仅接受各个参数并将它们放入数组中。您会注意到compose
函数返回了另一个函数。因此compose(omg)
将返回另一个等待value
的函数。提供值后,该功能将停止运行。我们用函数列表调用compose
,它返回一个等待值的函数。我们将该功能分配给const
processFile
。然后,我们执行异步操作并将processFile
设置为回调。回调(我们的compose
函数)接收它正在等待的值,然后同步进行所有处理。希望这可以清除一些问题。我还建议您查看Promise,这样您就不必处理回调。
JavaScript很有趣,因为它本身就是一种异步语言。另一方面,Python不是。这意味着在python中您可以使用eboth样式,但是在Javascript中,有时必须同时使用两种样式。
此外,请记住,在JavaScript中,存在一些回调,我们使用它们构造了Promise,我们使用了它们构造了Async / Await。了解回调流程对于能够更有效地使用更高级别的工具至关重要。
关于javascript - JS回调:延续传递还是糖果工厂风格?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52480246/