其他:
前面说到 curry,而 curry 与 compose(组合)是一对好基友,curry 函数的很大价值体现在它对于 compose 的友好性。组合的思想在于把小的单元逻辑合成一个程序,1+1>2。
在数学里,函数 f 和 g 的组合定义为 f( g(x) ),在 JavaScript 中就是这样了
var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
可以看成 x 在函数 f,g之间通过“管道”传输,来看一个例子。
var head = function(x) {
return x[0];
};
var reverse = reduce(function(acc, x) {
return [x].concat(acc);
}, []);
var last = compose(head, reverse);
last(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'uppercut'
你是否注意到 reverse 函数最先被应用?这很重要,函数是从右向左应用的。(从右往左执行更能反映数学上的定义)。组合函数有一个强大的特性——结合律。这意味着任何一个函数分组都可以拆开来,然后再以它们自己的组合方式打包在一起,这给我们带来了强大的灵活性。
var toUpperCase = function(x) {
return x.toUpperCase();
};
var exclaim = function(x) {
return x + '!';
};
compose(toUpperCase, compose(head, reverse));
// 或者
compose(compose(toUpperCase, head), reverse);
//因为如何为 compose 的调用分组不重要,所以结果都是一样的。这也让我们有//能力写一个可变的组合(variadic compose),用法如下:
// 前面的例子中我们必须要写两个组合才行,但既然组合是符合结合律的,我们// 就可以只写一个,
// 而且想传给它多少个函数就传给它多少个,然后让它自己决定如何分组。
var lastUpper = compose(toUpperCase, head, reverse);
lastUpper(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'UPPERCUT'
var loudLastUpper = compose(exclaim, toUpperCase, head, reverse)
loudLastUpper(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'UPPERCUT!'
// 或
var last = compose(head, reverse);
var angry = compose(exclaim, toUpperCase);
var loudLastUpper = compose(angry, last);
// 更多变种...
关于如何组合,并没有标准的答案——我们可以按自己喜欢的方式搭乐高积木。一般最佳实践是让组合可重用。
#compose与curry
来看一个例子:
HTML代码部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.11/require.min.js"></script>
<script src='flickr.js'></script>
</head>
<body>
</body>
</html>
js部分:
requirejs.config({
paths: {
ramda: 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
}
});
// prop 函数的实现
// var prop = _.curry(function(property, object){
// return object[property];
// });
require([
'ramda',
'jquery'
],
function(_, $) {
//utils
var Impure = {
getJSON: _.curry(function(callback, url) {
$.getJSON(url, callback);
}),
setHtml: _.curry(function(sel, html) {
$(sel).html(html);
})
};
var img = function(url) {
return $('<img/>', {
src: url
});
};
var trace = _.curry(function(tag, x) {
console.log(tag, x);
return x;
});
var url = function(t) {
return 'https://api.flickr.com/services/feeds/photos_public.gne?tags=' + t + '&format=json&jsoncallback=?';
};
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var srcs = _.compose(_.map(mediaUrl), _.prop('items'));
var images = _.compose(_.map(img), srcs);
var renderImages = _.compose(Impure.setHtml('body'), images);
var app = _.compose(Impure.getJSON(trace(renderImages), url);
// var app = _.compose(Impure.getJSON(trace(trace('response')), url);
app('cats');
});
通过 curry 与 组合 搭建起一个简单的应用,请求 Flickr 图片,并加载到页面上。。