Javascript JS回调:继续传递还是糖果工厂风格?
在编程风格课程中,我们被要求在“延续传递风格”和“糖果工厂风格”中实现一些代码 我们正在读的书是克里斯蒂娜·维德拉·洛佩斯(Cristina Videira Lopes)的《编程风格练习》(第5章和第8章) 我们被要求用另一种语言实现书中的示例代码(在书中是用Python编写的,现在我使用Javascript) 为了理解我的问题,我将向您展示书中显示的两个主要差异: 糖果工厂风格Javascript JS回调:继续传递还是糖果工厂风格?,javascript,callback,coding-style,styles,continuation-passing,Javascript,Callback,Coding Style,Styles,Continuation Passing,在编程风格课程中,我们被要求在“延续传递风格”和“糖果工厂风格”中实现一些代码 我们正在读的书是克里斯蒂娜·维德拉·洛佩斯(Cristina Videira Lopes)的《编程风格练习》(第5章和第8章) 我们被要求用另一种语言实现书中的示例代码(在书中是用Python编写的,现在我使用Javascript) 为了理解我的问题,我将向您展示书中显示的两个主要差异: 糖果工厂风格 #!/usr/bin/env python read_file(path_to_file): """
#!/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)
延续传球风格
#!/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)
Javascript
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写的是继续传递,因为函数是作为参数传递的。但同时,为了调用main函数,您使用了与candy factory相同的“管道式”调用
鉴于上面用JS编写的代码,如何实现candy factory风格?这段代码(基于回调)是candy factory还是continuation传递风格?我如何编写上面的代码,而不使用回调,同时又不信任JS?我认为您混淆了两件事。Candy样式通常被称为函数合成。也就是说,一个函数的输出就是下一个函数的输入
f(g(h(1)))
h(1)
输出一个值,这是g
的输入,它输出一个值,是f
的输入
这与Javascript中用于异步操作的回调样式不同
f(1,g)
其中f
获取一个值,对其进行处理,并在以后调用g
通常在JavaScript中,您需要处理异步操作,但在这些情况下,您只需要回调(continuations)。像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
的参数列表。…
(rest运算符)只接受单个参数并将它们放入数组中。您会注意到,compose
函数返回另一个函数。因此compose(omg)
将返回另一个函数,等待值。当您提供该值时,该函数将停止运行。我们使用函数列表调用compose
,它返回一个等待值的函数。我们将该函数分配给const
processFile
。然后我们执行异步操作并将processFile
设置为回调。回调函数(我们的compose
函数)接收它等待的值,然后同步执行所有处理
希望这能澄清一些事情。我还建议你考虑一下承诺,这样你就不必处理回电了
JavaScript很有趣,因为它本来就是一种异步语言。另一方面,Python并非如此。这意味着在python中可以使用eboth样式,但在Javascript中有时必须同时使用这两种样式
另外,请记住,在JavaScript中,存在回调,我们使用回调构建承诺,使用回调构建异步/等待。理解回调流对于能够更有效地使用更高级的工具是必不可少的。我想你会发现“candy factory”风格并不常用。我不知道这是什么意思。不过,您描述的TermFrequency
函数绝对是一种延续传递样式。如果您的实用程序函数更简单,例如,string\u to-lower
只接受一个字符串并返回其小写版本,那么您可以通过Promises或async
/wait
轻松地将其重写为明显更简单的函数。您无法摆脱底层的异步性。如果包含异步函数读取文件
,则与之相关的所有内容都必须是异步的。非常感谢您的回答。在接下来的几个小时里,我将进行更多的探索。不过,这是一个非常有用的解释