Javascript 如何像Elm那样在JS中使用管道函数?
我刚刚开始学习函数式编程,我很难弄明白如何做到这一点(如果这是值得的话)。我研究过咖喱,不确定这是否是我需要走的方向??还是管道 我想从一个值开始,然后通过不同的函数传递它。下划线具有类似的“chain”方法。然而,我不想使用原型来实现这一点。我意识到解决方案可能与我的目标语法不匹配 Elm的Javascript 如何像Elm那样在JS中使用管道函数?,javascript,underscore.js,elm,Javascript,Underscore.js,Elm,我刚刚开始学习函数式编程,我很难弄明白如何做到这一点(如果这是值得的话)。我研究过咖喱,不确定这是否是我需要走的方向??还是管道 我想从一个值开始,然后通过不同的函数传递它。下划线具有类似的“chain”方法。然而,我不想使用原型来实现这一点。我意识到解决方案可能与我的目标语法不匹配 Elm的|>语法(如下)非常好看 // what i'd like to do (or similar) in JS *without using prototype* num = ("(123) 456-7890
|>
语法(如下)非常好看
// what i'd like to do (or similar) in JS *without using prototype*
num = ("(123) 456-7890")
.removeDashes()
.removeParens()
.removeSpaces()
// what elm does
"(123) 456-7890"
|> removeDashes
|> removeParens
|> rem
// functions I wrote so far
removeDashes = function(str) {
return str.replace(/-/g, '');
};
removeParens = function(str) {
return str.replace(/\(|\)/g, '');
};
removeSpaces = function(str) {
return str.replace(/\s/g, '');
};
// what i'm currently doing
num =
removeDashes(
removeParens(
removeSpaces(
"(123) 456-7890"")));
正如hindmost所说,研究使用原型。字符串原型允许您向所有字符串添加类级功能:
String.prototype.removeParens = function() {
this = this.replace(/\(|\)/g, '');
}
这使您可以执行以下操作:
var myString = "(test)";
myString.removeParens();
myString.removeDashes().removeParens().removeSpaces();
一旦您将其他函数添加到字符串原型中,您就可以像这样简单地链接函数调用:
var myString = "(test)";
myString.removeParens();
myString.removeDashes().removeParens().removeSpaces();
等等。最简单的方法就是将它们添加到原型链中,但是你可以用一个对象来完成。下面是一个简单的例子:
function MyString( str ){
var value = str.toString();
return {
removeDashes: function() {
value = value.replace(/-/g, '');
return this;
},
removeParens: function() {
value = value.replace(/\(|\)/g, '');
return this;
},
removeSpaces: function() {
value = value.replace(/\s/g, '');
return this;
},
toString: function (){
return value;
},
valueOf: function (){
return value;
}
};
}
稍后您可以执行以下操作:
var clean = (new MyString('This \\(Is)\/ Dirty'))
.removeDashes()
.removeParens()
.removeSpaces();
这样,您将保持
原型的清洁。要检索非对象值,只需运行toStrong()
方法,toValue()
或对该值执行任何操作(contatenating 1,divide it,anything!)。正如其他人所说,将函数添加到字符串原型中是一个有效且简短的解决方案。但是,如果您不想将它们添加到字符串原型中,或者如果您想在将来执行更复杂的函数,另一种选择是制作一个包装器来处理此问题:
function SpecialString(str){
this.str = str;
this.removeDashes = function() {
this.str=this.str.replace(/-/g, '');
return this;
};
this.removeParens = function() {
this.str=this.str.replace(/\(|\)/g, '');
return this;
};
this.removeSpaces = function() {
this.str=this.str.replace(/\s/g, '');
return this;
};
return this;
}
num = new SpecialString("(123) 456-7890").removeDashes().removeParens().removeSpaces();
console.log(num) // 1234567890
下面是我在lodash中找到的一个解决方案,它允许您混合自己的函数,然后使用它们来对抗链:
...
removeSpaces = function(str) {
return str.replace(/\s/g, '');
};
_.mixin({
removeSpaces: removeSpaces,
removeParens: removeParens,
removeDashes: removeDashes
});
num = _.chain("(123) 456-7890")
.removeSpaces()
.removeParens()
.removeDashes()
.value()
有不同的方法来解决这个问题,您在下划线和Elm中提供了参考
在Elm中,curried函数是方程的重要组成部分。由于每个函数都接收一个参数,因此可以构建部分应用这些参数的链,等待管道中编织的参数。这同样适用于Haskell、PureScript及其同类语言
在JavaScript中复制ipsis literis需要一点糖分——您可以使用a来获得实现这一点的源代码转换
没有糖,它可以走很多路。也许探索的一种方法是使用生成器,将解析链的位向下传递,直到得到一个非函数值。这不是一个非常严肃的建议,但是一个可行的建议:
var update = pipe()(removeDashes >> removeParens >> removeSpaces);
update("(123) 456-7890"); //=> "1234567890"
这是基于管道的此实现的:
var pipe = function() {
var queue = [];
var valueOf = Function.prototype.valueOf;
Function.prototype.valueOf = function() {
queue.push(this);
return 1;
};
return function() {
Function.prototype.valueOf = valueOf;
return function(x) {
for (var i = 0, val = x, len = queue.length; i < len; i++) {
val = queue[i](val);
}
return val;
}
};
};
var pipe=function(){
变量队列=[];
var valueOf=Function.prototype.valueOf;
Function.prototype.valueOf=函数(){
排队。推(这个);
返回1;
};
返回函数(){
Function.prototype.valueOf=valueOf;
返回函数(x){
对于(var i=0,val=x,len=queue.length;i
您可以在我的演讲中看到更多内容。如果您想了解JavaScript中的函数编程,我建议您使用下划线、Lodash或Ramda之类的库。它们都具有合成/管道功能。大多数情况下,您可能希望将它与这些库还提供的某种形式的部分应用程序相结合
无论如何,尝试自己实现它是一个很好的练习。
我会像这样解决它
/* Asumes es5 or higher */
function pipe (firstFn /* ...restFns */) {
var _ = null;
var _slice = Array.prototype.slice;
var restFns = _slice.call(arguments, 1) || [];
return function exec_fns() {
var args = _slice.call(arguments, 0, 1);
return restFns.reduce(function(acc, fn) {
return fn.call(_, acc);
}, firstFn.apply(_, args));
}
}
removeDashes = function(str) {
return str.replace(/-/g, '');
};
removeParens = function(str) {
return str.replace(/\(|\)/g, '');
};
removeSpaces = function(str) {
return str.replace(/\s/g, '');
};
console.log(pipe(
removeDashes,
removeParens,
removeSpaces
)("(123) 456-7890") == "1234567890")
Fogus的Functional JavaScript也是深入研究这种编程风格的好资源您可以在一行中创建管道函数,具有良好的可读性:
const pipe = (...fns) => fns.reduce((v, f) => v.constructor === Function ? v() : f(v));
它将以这种方式使用:
var numResult = pipe('(123) 456-7890', removeDashes, removeParens, removeSpaces);
var管道=(…fns)=>fns.reduce((v,f)=>v.constructor==函数?v():f(v));
功能移除灰烬(str){
返回str.replace(/-/g');
}
功能删除参数(str){
返回str.replace(/\(|\)/g',);
}
函数RemoveSpace(str){
返回str.replace(/\s/g');
}
console.log(
'结果:',管道('(123)456-7890',移除灰,移除帕伦斯,移除空间)
);代码>将这些函数添加到字符串的原型中。你可以用链式的方式调用它们。链式编程与函数式编程完全不同。@torazaburo是的,我想我要找的是管道而不是链式编程。Lodash有一个管道函数,看起来像是closestRamda.js管道或compose函数,可用于执行此活动。假设您打算推广向字符串原型添加方法,您至少应该使用Object.defineProperty(String.prototype,'removeParens',…)
这样它们就不会被枚举。这样做很有效,但我一直在寻找更多的管道而不是链接(我的错误)这很有趣,但对我来说,它的可读性不如仅仅打开包装。谢谢@SkinnyG33k我只是提供了一个不同的选择。使用原型的版本可以工作,但是如果原型发生了变化,它可能会把东西弄脏。除此之外,原型更优越。这可以工作,但我正在寻找更多的管道而不是链接(我的错误)@skinyg33k检查我想这可以修改为允许管道('(123)456-7890')(RemoveDash>>removeParens>>RemoveSpace);//=>'1234567890'
,甚至可能自动curry它以允许任何一种样式。但这总让人觉得更像是一个把戏,而不是生产代码。那些宏非常漂亮!我在下面的回答中使用了下划线的mixin和chain。足够近,我不必在fns中使用此
。