Javascript JS回调:继续传递还是糖果工厂风格?

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): """

在编程风格课程中,我们被要求在“延续传递风格”和“糖果工厂风格”中实现一些代码

我们正在读的书是克里斯蒂娜·维德拉·洛佩斯(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)
延续传球风格

#!/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
轻松地将其重写为明显更简单的函数。您无法摆脱底层的异步性。如果包含异步函数
读取文件
,则与之相关的所有内容都必须是异步的。非常感谢您的回答。在接下来的几个小时里,我将进行更多的探索。不过,这是一个非常有用的解释