JS 回调:延续传递还是糖果工厂风格?
JS Callbacks: continuation-passing or candy factory style?
在编程风格课程中,我们被要求在“持续传递风格”和“糖果工厂风格中实现一些代码”。
我们正在阅读的这本书 "Exercises in programming styles" 作者是 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)
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中写的是Continuation passing,因为函数是作为参数传递的。但与此同时,为了调用主函数,您使用与糖果工厂相同的调用 "in pipeline style"。
上面用JS写的代码是怎么实现糖果工厂风格的?该代码(基于回调)是糖果工厂还是延续传递风格?如何在不使用回调的情况下编写上面的代码,同时信任 JS?
我认为你混淆了两件事。糖果风格通常被称为功能组合。这就是一个函数的输出是下一个函数的输入的地方。
f(g(h(1)))
h(1)
输出一个值,这是 g
的输入,它输出一个值,是 f
的输入。
这与 Javascript 中用于异步操作的回调样式不同。
f(1,g)
其中 f
获取一个值,对其进行处理并稍后调用 g
。
通常在 JavaScript 中,您需要处理异步操作,但在这些情况下您只需要回调(延续)。像你的 stringToLower
这样的函数只需要 return 数据。
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
函数 return 是另一个函数。所以 compose(omg)
会 return 另一个等待 value
的函数。当您提供该值时,该函数将关闭 运行。我们用函数列表调用 compose
,它 return 是一个等待值的函数。我们将该函数分配给 const
processFile
。然后我们进行异步操作并将 processFile
设置为回调。回调(我们的 compose
函数)接收它正在等待的值,然后同步进行所有处理。
希望这能解决一些问题。我还建议您查看 promises,这样您就不必处理回调。
JavaScript 很有趣,因为它本身就是一种异步语言。 Python 另一方面不是。这意味着在 python 中您可以同时使用两种样式,但在 Javascript 中有时您必须同时使用这两种样式。
另外,请记住,在 JavaScript 中,有回调,我们用它来构建承诺,我们用它来构建 Async/Await。了解回调流程对于能够更有效地使用更高级别的工具至关重要。
在编程风格课程中,我们被要求在“持续传递风格”和“糖果工厂风格中实现一些代码”。
我们正在阅读的这本书 "Exercises in programming styles" 作者是 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)
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中写的是Continuation passing,因为函数是作为参数传递的。但与此同时,为了调用主函数,您使用与糖果工厂相同的调用 "in pipeline style"。
上面用JS写的代码是怎么实现糖果工厂风格的?该代码(基于回调)是糖果工厂还是延续传递风格?如何在不使用回调的情况下编写上面的代码,同时信任 JS?
我认为你混淆了两件事。糖果风格通常被称为功能组合。这就是一个函数的输出是下一个函数的输入的地方。
f(g(h(1)))
h(1)
输出一个值,这是 g
的输入,它输出一个值,是 f
的输入。
这与 Javascript 中用于异步操作的回调样式不同。
f(1,g)
其中 f
获取一个值,对其进行处理并稍后调用 g
。
通常在 JavaScript 中,您需要处理异步操作,但在这些情况下您只需要回调(延续)。像你的 stringToLower
这样的函数只需要 return 数据。
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
函数 return 是另一个函数。所以 compose(omg)
会 return 另一个等待 value
的函数。当您提供该值时,该函数将关闭 运行。我们用函数列表调用 compose
,它 return 是一个等待值的函数。我们将该函数分配给 const
processFile
。然后我们进行异步操作并将 processFile
设置为回调。回调(我们的 compose
函数)接收它正在等待的值,然后同步进行所有处理。
希望这能解决一些问题。我还建议您查看 promises,这样您就不必处理回调。
JavaScript 很有趣,因为它本身就是一种异步语言。 Python 另一方面不是。这意味着在 python 中您可以同时使用两种样式,但在 Javascript 中有时您必须同时使用这两种样式。
另外,请记住,在 JavaScript 中,有回调,我们用它来构建承诺,我们用它来构建 Async/Await。了解回调流程对于能够更有效地使用更高级别的工具至关重要。