Javascript函数柯里化

函数柯里化currying,是函数式编程非常重要的一个标志。它的实现需要满足以下条件,首先就是函数可以作为参数进行传递,然后就是函数可以作为返回值return出去。我们依靠这个特性编写很多优雅酷炫的代码。那我们来看一下最简单的实现。

大家一般都是举addSum的例子,我当然也不例外。

1
2
3
4
5
6
7
8
9
add = (num1)->
return (num2)->
return num1 + num2;

add3 = add(3);
add5 = add(5);

add3(5) # 返回8
add5(5) # 返回10

上述例子其实已经对柯里化的实现,有一个非常好的了解了。其实也就是“分步求值”,我们可以把第一个参数通过闭包保存起来,以供return出去的匿名函数使用。所以我们可以根据add来自定义各种各样的新函数。


我们要使某个函数可以柯里化,难道一定要在函数创建时,就具有柯里化的特性么?假设我们的add函数,起初并不具有柯里化特性的,我们需要怎么做才能让它柯里化呢?

阅读全文 »

binarySearch二分查找——Javascript实现

在很早之前,我就写过了一篇也关于二分法的相关博文:JavaScript快排与原生sort的测试。当时是用二分法进行快速排序,其实和这次思路大致相当。二分查找最重要的一个条件,就是需要将数组先按照从小到大的顺序进行排序后,方可进行查找。

一起来想想大致的思路:

1
2
3
4
1. binarySearch函数需要接收的参数是:一个预先排序好的数组,一个需要查找的目标值,左边界和右边界。
2. 让数组的中值和目标值比较,若相等,则返回中值所在的序号,函数结束。若不相等,进行第三步
3. 不相等,则进行大小比较,若目标比中值小,则范围缩小到左边界到中值,并重新返回第一步。反之,范围缩小到中值到右边界,返回第一步。
4. 找不到则是,中值序号等于左边界或右边界,返回-1

这个伪代码,写的还是有点凌乱的。最主要的思想还是在于递归。一起实现上述的代码吧。我用的是coffeeScript实现的,除了某些地方我会指出来,大致都能看懂的。若对coffeeScript感兴趣的同学,可以移步:CoffeeScript中文。还是要强调一下,coffee的块级判断是靠缩进层次的。所以使用时,千万不要空格和Tab缩进混用。

1
2
3
4
5
6
7
8
9
10
11
12
13
binarySearch = (dataArr, target, start = 0, end = dataArr.length-1)->
# 若不传参,参数默认: start = 0, end = dataArr.length - 1

middle = parseInt((start + end) / 2) # 取中间序号
return -1 if middle == start or middle == end # 查询失败

if(dataArr[middle] == target)
return middle # 查询成功

if(target < dataArr[middle])
return binarySearch(dataArr, target, 0, middle-1)
else
return binarySearch(dataArr, target, middle+1, end)
阅读全文 »

KMP算法——Javascript实现

腾讯和阿里的笔试刚过去了,里面有很多题都很值得玩味的。之前Blog积累的很多东西,还要平时看的书,都有很大的帮助。这个深有体会啊!

例如,腾讯有一道算法题是吃香蕉(好邪恶的赶脚..),一次吃一根或者两根,50根香蕉可以有多少种吃法?当时我一看尼玛,不就是我之前总结过的:递归算法,JavaScript实现。里面的走楼梯的问题,我到现在还是记得的。(但是为了抗议我对卷纸的不专业性,我用CoffeeScript实现了算法…感觉可能会因此跪下。)然后就是有一道选择题,考的是Javascript的闭包陷阱,我一看尼玛,不是我之前总结过的:循环闭包的影响以及其解决方案。我也是一模一样用setTimeout去模拟的。简直不能再爽。当然,也不得不说,腾讯到最后也只有这两题和前端有一点联系。

相比之下,阿里就好很多了。虽然时间很紧,题目很多,但起码不会一抬眼全是熟悉的陌生人。印象比较深的是《Javascript设计模式》里的观察者模式,还有《Javascript高级程序设计》里的有关CookieUtil的。。但是,我有一题,完全不记得如何做了。那就是今天的主角,KMP算法!


上面扯淡完毕了。个人博客嘛,随心所欲啦。先给参考资料的地址:字符串匹配的KMP算法。这个是阮一峰老师的博文,算是写的很不错的了。想看生动形象的博文的同学可以直接移步过去。

那这个用于字符串匹配的KMP算法到底怎么用的呢。我们先看看需求:字符串A=”BBCABCDABABCDABCDABDE”里如何快速匹配到a=“ABCDABD”。用伪代码来写这些步骤应该是这样的:

  1. 字符串的首位与子字符串的首位进行匹配,匹配失败,则字符串后移继续匹配。匹配成功,则字符串与子字符串一起后移,继续匹配。
  2. 继续匹配的过程中,最理想的状态便是从头到尾成功,然后匹配过程也就结束了。倘若中途有不匹配的,子字符串就要回滚。

问题来了:子字符串回滚到哪儿?若是回滚到匹配开始的下一位,那当然是可以的,只不过是做了很多的无用功。所以KMP算法就是为了这个时候诞生的,可以有效的提高效率。

这里我用阮老师的一张图更好的解释一下。
img
我们可以看到,最佳的回滚位置应该是让子字符串的“C”对应空格。这样我们才可以最优化的处理重复的“AB”这个东西。

直接看一个公式:回滚位数 = 已匹配的字符数 - 对应的部分匹配值。我们可以看到已经匹配的字符数是6,然后最佳的回滚位数是4,那么对应的部分匹配值应该是2,那这个2是怎么来的?

阅读全文 »

gulp之静态资源防缓存处理

  最近,因为校友网项目开始有些规模了。开始就要考虑对静态资源进行工程自动化的管理。一讲到前端的自动化工具,大家或许都会想到Grunt,Gulp,或者百度的FIS。这三个都有各自的特点,大家可以依据自己的喜好,选择工具。至于为什么选择Gulp,因为Grunt的gruntfile配置真的很头大好吗!简直看到头晕晕,但是还是有不少人喜欢这种方式的。然后FIS真心很强大,你所需要的,基本它都提供了,并且做得很好很简单,如果你急于马上使用可以赶紧去看看。而我为什么不用呢,感觉可能是因为,有点黑盒子?哈哈哈….不说了,让我们赶紧看看今天的主角——Gulp。

  定义:gulp.js 是一种基于流的,代码优于配置的新一代构建工具。

  关于还要安装Node,怎么样用npm加载需要的Node模块,就不再赘述啦。当然使用yeoman来搭建手脚架是最快的,有兴趣的可以看看,慕课网里有噢。接下来看看我们的案例。我们的需求是,为了防止客户端的静态资源缓存,我们需要每次更新css或js的时候,通过md5或时间戳等方式重新命名静态资源。让客户端可以重新请求资源,而不是从缓存里取。然后html模板里的src也要做相应的修改。当然,这里还有个附加的需要就是,静态资源需要自行优化(压缩合并)。

  1. 静态资源优化
  2. 静态资源重命名
  3. 修改静态资源的引用路径

  若是我们手动修改,这会有多麻烦呢?大家可以想一想,我们先用工具压缩了资源,然后手动更改名字,再打开相应的页面,改路径。这样一直枯燥的重复,不仅容易出错,而且尼玛工作量很大好嘛?!程序员自有懒人在,我们就站在懒巨人的肩膀上,沐浴春风。

  那在gulpfile中,我们要用到的插件有哪些呢?

  

  require完之后,我们就可以开始编写一个简单实用的版本控制的自动化工具了。

阅读全文 »

递归算法,JavaScript实现

  我们先来看一下定义。递归算法,是将问题转化为规模缩小的同类问题的子问题,每一个子问题都用一个同样的算法去解决。一般来说,一个递归算法就是函数调用自身去解决它的子问题。

  递归算法的特点:

  1. 在函数过程中调用自身。
  2. 在递归过程中,必须有一个明确的条件判断递归的结束,既递归出口。
  3. 递归算法简洁但效率低,通常不作为推荐算法。

  上面这些是百度百科的解释,讲的也是十分明确,大家配合实例来细细琢磨。


  阶乘

  问题描述: n! = n(n-1)…2*1

  代码实现:

  

  我们拿到问题的时候,我们按照定义的说明,可以先将规模缩小到同类的子问题。比如,n! 是等于 n (n-1)!,然后(n-1)! = (n-1)(n-2)!。这样依次往下推,直到if的出口。这里用了arguments.callee,是为了防止函数名的紧密耦合,在这里它等同于factorial(n-1)。函数实现起来是不是简洁明了呢。当然因为问题规模简单,其实用循环也是可以实现的,大家可以尝试一下。


  斐波那契数列

  问题描述:1, 1, 2, 3, 5, 8, 13, 21, 34, ……. 求第n个数是多少。

  代码实现:

  

  其实用刚才的想法实现,也是非常的简单的。通过分析可以得到第n个数,是前两个数的和,通过这个我们就可以通过递归,不断获得所需要的前两个数,直到n<= 2这个条件返回1。
  

阅读全文 »