Javascript 以函数样式应用于字符串的函数序列

Javascript 以函数样式应用于字符串的函数序列,javascript,functional-programming,Javascript,Functional Programming,我试图理解在实践中使用JavaScript函数风格的方法。我已经创建了一组简单的函数来处理字符串,但我觉得我在概念上做得不对,因为它看起来就像命令式风格,即使我不改变输入,也不改变函数中应用程序的状态 下面是它的外观: var LineParser = require('../modules/LineParser'); var inputLine = 'A line with multiple spaces'; var outputLine = LineParser()

我试图理解在实践中使用JavaScript函数风格的方法。我已经创建了一组简单的函数来处理字符串,但我觉得我在概念上做得不对,因为它看起来就像命令式风格,即使我不改变输入,也不改变函数中应用程序的状态

下面是它的外观:

var LineParser = require('../modules/LineParser');
var inputLine = 'A line with     multiple          spaces';
var outputLine = LineParser().formatSpaces(inputLine);
//  'A line with multiple spaces'
outputLine = LineParser().capitalize(outputLine);
//  'A Line With Multiple Spaces'
outputLine = LineParser().formatSomethingElse(outputLine);
//  Some more formatting, then do further processing with outputLine
如果我使用回调运行序列,当我有10个简单的处理函数时,它会很快变成一组丑陋的嵌套回调

如果我添加方法链接,原型方法的想法也会与函数样式相反,因为链中的函数将依赖于以前的状态,而不仅仅依赖于它们获得的输入

我应该怎么做才能让它看起来更美观,更实用

更新:经过更深入的研究,我发现这个主题名为。这似乎是问题的正确解决方案,也是功能世界的基本内容之一。 下面是我用来将多个函数组合成一个函数的函数:

var compose = function () {
    var funcs = arguments;
    return function () {
        var args = arguments;
        for (var i = funcs.length; i-- > 0;) {
            args = [funcs[i].apply(this, args)];
        }
        return args[0];
    };
};
然后我做一篇作文:

var composedFunction = compose(func1, func2, ..., funcn)

从右到左运行,一切正常。

如果您想要异步编程,但不喜欢嵌套回调,您考虑过lib吗? 你可以有这样的东西:

var LineParser = require('../modules/LineParser');
var inputLine = 'A line with     multiple          spaces';

async.waterfall([
    function(callback) {
        var result = LineParser().formatSpaces(inputLine);
        callback(null, result);
    },
    function(arg1, callback) {
        var result = LineParser().capitalize(arg1);
        callback(null, result);
    },
    function(arg1, callback) {
        var result = LineParser().formatSomethingElse(arg1);
        callback(null, result);
    }
], function (err, result) {
    // retrieve the final string
});

如果您将LineParser方法修改为异步方法,这将变得非常有用,否则它只会使您的3行代码变重

如果您想要异步编程,但不喜欢嵌套回调,您考虑过lib吗? 你可以有这样的东西:

var LineParser = require('../modules/LineParser');
var inputLine = 'A line with     multiple          spaces';

async.waterfall([
    function(callback) {
        var result = LineParser().formatSpaces(inputLine);
        callback(null, result);
    },
    function(arg1, callback) {
        var result = LineParser().capitalize(arg1);
        callback(null, result);
    },
    function(arg1, callback) {
        var result = LineParser().formatSomethingElse(arg1);
        callback(null, result);
    }
], function (err, result) {
    // retrieve the final string
});

如果您将LineParser方法修改为异步方法,则可以将其转化为有用的内容,否则它只会使您的3行变得更重

您的LineParser似乎有formatSpaces、capitalize和formatSomethingElse等方法。您可以做的最简单的事情是使所有这些方法都返回此值,这样您就可以像这样链接这些方法:

var outputline = LineParser.formatSpaces(inputLine).capitalize().formatSomethingElse()

尽管从外观上看,所有方法都需要一些字符串作为参数,因此您可能需要进行一些实现更改,例如,如果给定,则将字符串保存在私有变量中,如果未给定参数,则从变量中拉入字符串。

您的lineparser似乎有formatSpaces之类的方法,大写并格式化一些东西。您可以做的最简单的事情是使所有这些方法都返回此值,这样您就可以像这样链接这些方法:

var outputline = LineParser.formatSpaces(inputLine).capitalize().formatSomethingElse()

尽管从外观上看,所有方法都需要一些字符串作为参数,因此您可能需要进行一些实现更改,例如,如果给定,则将字符串保存在私有变量中,如果未给定参数,则从变量中拉入字符串。

函数式,即,我们在Lisp等中看到的纯函数式样式如下所示:

var outputline = formatSomethingElse(capitalize(formatSpaces(inputline)));
通常,为了便于阅读,其格式为:

var outputline = formatSomethingElse(
    capitalize(
        formatSpaces(inputline)
    )
);
任何其他形式都不是功能性风格。函数样式有点像反向波兰符号,因为所述操作应以反向读取。实际上,RPN本身就是一种函数式编程语法,如Forth所体现的那样

有一种样式的外观与功能样式相似:方法链接:

var outputline = LineParser(inputline)
    .formatSpaces()
    .capitalize()
    .formatSomethingElse()
    .toString();
与函数样式不同,方法链接是按顺序读取的

虽然最著名的链接库jQuery改变了对象的状态,但实际上没有必要这样做。以LineParser的以下简单实现为例:


函数式风格,即我们在Lisp等中看到的纯函数式风格,如下所示:

var outputline = formatSomethingElse(capitalize(formatSpaces(inputline)));
通常,为了便于阅读,其格式为:

var outputline = formatSomethingElse(
    capitalize(
        formatSpaces(inputline)
    )
);
任何其他形式都不是功能性风格。函数样式有点像反向波兰符号,因为所述操作应以反向读取。实际上,RPN本身就是一种函数式编程语法,如Forth所体现的那样

有一种样式的外观与功能样式相似:方法链接:

var outputline = LineParser(inputline)
    .formatSpaces()
    .capitalize()
    .formatSomethingElse()
    .toString();
与函数样式不同,方法链接是按顺序读取的

虽然最著名的链接库jQuery改变了对象的状态,但实际上没有必要这样做。以LineParser的以下简单实现为例:


评论你的编辑。函数compose在原理上很实用,但在实现上却不实用。实际上,该函数变异了一些变量,例如i和args,因此不是完全函数

为了避免使用这些变异变量,您可以递归地定义compose。另一个解决方案是依赖第三方函数库,例如下划线js,它已经定义了组合函数


另外一个明显的注释:要使代码具有compose的功能,函数func1、func2、,。。。这是组成,不应该改变他们的论点

评论您的编辑。函数compose在原理上很实用,但在实现上却不实用。英德 d、 该函数变异了一些变量,例如i和args,因此不是完全功能的

为了避免使用这些变异变量,您可以递归地定义compose。另一个解决方案是依赖第三方函数库,例如下划线js,它已经定义了组合函数


另外一个明显的注释:要使代码具有compose的功能,函数func1、func2、,。。。这是组成,不应该改变他们的论点

我试过了,但它似乎不符合功能性风格,因为它依赖于以前的应用程序状态。@SergeiBasharov也许你可以探索类似的东西是如何工作的?JavaScript中的Functional通常意味着使用map、reduce和filter,而不是在数组中循环。@SergeiBasharov哦,如果你想学习Functional JavaScript,你的这个lineparser可能不是最好的主意。如果我是你,我可能只会学习一些函数库是如何制作的。我尝试过,但由于依赖以前的应用程序状态,它似乎与函数式风格背道而驰。@SergeiBasharov也许你可以探索类似的东西是如何工作的?JavaScript中的Functional通常意味着使用map、reduce和filter,而不是在数组中循环。@SergeiBasharov哦,如果你想学习Functional JavaScript,你的这个lineparser可能不是最好的主意。如果我是你,我可能会学习一些函数库是如何制作的。虽然异步编程是一种函数式编程,但函数式编程不是异步编程。“这就像声称汽车就是汽车,完全忽略了非汽车的汽车,如卡车、自行车、船只和飞机。”斯莱贝特曼:好的,但这是否让我的回答偏离了主题?如果您认为我写的东西不正确,请随意编辑文本。异步编程是函数式的,函数式不是异步编程。“这就像声称汽车就是汽车,完全忽略了非汽车的汽车,如卡车、自行车、船只和飞机。”斯莱贝特曼:好的,但这是否让我的回答偏离了主题?如果您认为我写了一些不正确的东西,请随意编辑文本。如果我使用回调运行序列,您的意思是什么?你为什么要那样做?你的字符串函数是异步的吗?我不明白为什么原型方法会与函数风格相反。作为方法,它们可能以非常规语法获取对象的第一个参数,但它们也只是函数。它们特别适用于不可变对象。为什么LineParser是一个函数,为什么需要调用它,它做什么?回调是指将函数作为参数传递给其他函数,在函数编程环境中可能使用了错误的术语,就像FormatSomethingElsFormatSpaceSinPutline;。LineParser只是一个返回其他函数的函数,可以转换为包含函数的对象。是的,这是错误的术语,可能是错误的理解。外部函数调用的参数本身是通过对函数调用求值创建的,但您仍然在传递字符串,而不是函数,因此这里没有回调。实际上,我认为这种方法在函数式编程中是相当标准的,不会变得难看:-如果我使用回调来运行序列,这是什么意思?你为什么要那样做?你的字符串函数是异步的吗?我不明白为什么原型方法会与函数风格相反。作为方法,它们可能以非常规语法获取对象的第一个参数,但它们也只是函数。它们特别适用于不可变对象。为什么LineParser是一个函数,为什么需要调用它,它做什么?回调是指将函数作为参数传递给其他函数,在函数编程环境中可能使用了错误的术语,就像FormatSomethingElsFormatSpaceSinPutline;。LineParser只是一个返回其他函数的函数,可以转换为包含函数的对象。是的,这是错误的术语,可能是错误的理解。外部函数调用的参数本身是通过对函数调用求值创建的,但您仍然在传递字符串,而不是函数,因此这里没有回调。实际上,我认为这种方法在函数式编程中是相当标准的,不会变得难看:-