函数式编程(三):组合函数

其他:

函数式编程(一):纯函数

函数式编程(二):curry

 

前面说到 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 图片,并加载到页面上。。

posted @ 2015-12-12 10:00  hwencc  阅读(1293)  评论(0编辑  收藏  举报