最近的几个月,我一直在学习一种叫Haskell的编程语言。由于里面有太多的从未遇到的编程概念,整个过程就像是完全重新学习如何编程。在i.TV网站上,我写了很多JavaScript(node.js和前端代码)。虽然有不少的函数式/haskell式的编程模式不能引用进来,但仍有大量的技术思想让我在使用javascript编程语言时受益不少。
你会发现Haskell库里有能够处理各种事情的各种各样的函数。起初我以为这些只是一种技术上的积累,但随后我认识到,这些函数相比起其它语言里的函数,它们能应用到形式更广泛的问题中。这使得它们更有价值,因为我们都不太喜欢对一些常见的问题还不得不自己去写解决方案。
这些函数是可以相互组合的:它们能针对性的解决某些问题,而不对你的代码做任何依赖,所以,你可以拼装它们,组合成一个能够解决你的大问题的东西。
高阶函数(Higher Order Functions)
在Haskell语言中,最多的被反复使用的函数都是高阶函数(higher order functions)——能以函数作为参数、能返回函数的函数。这使得它们具有固有的灵活性。下面是一个不太灵活的函数:它计算一个数组里等于某个值的元素的个数。
// 不灵活
function countMatching(array, value) {
var counted = 0
for (var i = 0; i < array.length; i++) {
if (array[i] == value)
counted++
}
return counted
}
// == 2
countMatching([1,3,3,4,5], 3)
它不灵活,因为它只能用来计算一个数组中精确匹配某个值的元素的个数。
下面是一个灵活一些的版本,它能接受一个函数,而不是一个值,作为参数。我们可以用它来对任何数据、任何对象进行比较。
// more flexible
function count(array, matching) {
var counted = 0
for (var i = 0; i < array.length; i++) {
if (matching(array[i]))
counted++
}
return counted
}
// == 2, same as first example
count([1,3,3,4,5], function(num) {
return (num == 3)
})
// == 2, now we can use our functions for ANY kind of items or match test!
count([{name:"bob"}, {name:"henry"}, {name:"jon"}], function(obj) {
return (obj.name.length < 4)
})
因为高阶函数更具灵活性,你就更少有机会去写它们,因为你一旦你写成一个,你可以它应用到各种不同的情况中。
可重复利用的比较函数
你可能注意到了,count
函数的写法比countMatching
更冗长。但是,虽然count
函数可复用了,但比较函数2却不可复用。如果是一些简单的情况,这就足够了,但经常,我们会需要更复杂的比较方法的函数。这样的函数不仅仅可用于计数,它们可以用于任何事情上,一但你写成或找到了这样的函数,从长期的角度看,它们会节省你大量的时间和调试功夫。
让我们来定义一个可复用的比较函数,达到我们的目的。==
不是一个函数。我们是否可以定义一个eq
函数来帮我们完成类似的事情呢?
function eq(a, b) {
return (a == b)
}
count([1,3,3,4,5], function(num) {
return eq(3, num)
})
我们向前迈进了一步:我们用了一个库函数来完成比较任务,而不是使用我们现写的代码。如果eq
函数很复杂,我们可以测试它并可以在其它的地方复用它。
但这使代码变得冗长,因为count
函数的参数是一个只需要一个参数——数组元素——的函数,而eq
函数却需要两个参数。我还是要定义我们自己的匿名函数。让我们来简化一下这些代码。
function makeEq(a) {
// countMatchingWith wants a function that takes
// only 1 argument, just like the one we're returning
return function(b) {
return eq(a, b)
}
}
// now it's only on one line!
count([1,3,3,4,5], makeEq(3))
我们写了一个兼容count
函数的函数(一个参数——数组元素——返回true或false)。看起来就像是count
函数调用的是eq(3, item)
。这叫做偏函数用法(partial function application)。