最近在看《高性能javascript》这本书,受益匪浅。相信很多朋友都看过这本书。里面在流程控制和算法一章提到了一个减少循环次数的算法 - duff's device。原文中提到的两个版本的代码,无论是看,还是实际运行,都有问题:
原版
[code=JScri]
//要执行的循环次数
var time = 12,
iterations = Math.floor( time / 8 ),
startAt = time % 8; do{
switch(startAt){
case 0: alert('hello');
case 7: alert('hello');
case 6: alert('hello');
case 5: alert('hello');
case 4: alert('hello');
case 4: alert('hello');
case 3: alert('hello');
case 2: alert('hello');
}
startAt = 0;
}while(--iterations);
[/code]改进版
[code=JScri]
//要执行的循环次数
var time = 12,
i = time % 8; while(i){
alert(i--);
} i = Math.floor(time / 8); while(i){
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
}
[/code]
上面的代码其实是我对书中的原本代码做了些改动,为了便于运行,但请放心我绝对没有改变算法的本质。
两段代码我觉得都有问题,但又不敢质疑权威。而且这本书提供别的代码都是正确的。网上搜索的duff's device的js版也都是这样写的。所以还是怀疑自己理解有问题。先看原版的。假如我需要调用alert函数12次。那么Math.floor(time)返回给iterations的值肯定是1。到底部的while(--iterations)的时候,直接就退出循环了,也就是说这个do{}while只会执行一次。那么调用到alert的次数是不对的,只有4次而已。再看改进版的。i = Math.floor(time / 8);i的值肯定也是1,那么下面的while(i)就会变成死循环了,因为js中0为fasle,非0为true,而i在循环体中每次调用alert(i--)会另i成为负数。就永远不会变为0了。两段代码存在的问题都无法执行成功,如果改一下就没问题了。
原版的将while(--iterations)改为while(iterations--)。
改进版的将下面的while(i)改为while(i>0)
不知各位理解了没,实在不明白到底是谁错了。
下面我在给出书中原本的代码:
原版
[code=JScri]
var iterations = Math.floor(items.length / 8),
startAt = items.length % 8,
i = 0; do{
switch(startAt){
case 0: process(items[i++]);
case 7: process(items[i++]);
case 6: process(items[i++]);
case 5: process(items[i++]);
case 4: process(items[i++]);
case 3: process(items[i++]);
case 2: process(items[i++]);
case 1: process(items[i++]);
}
startAt = 0;
}while(--iterations);
[/code]
改进版
[code=JScri]
var i = items.length % 8;
while(i){
process(items[i--]);
} i = Math.floor(items.length / 8); while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}
[/code]
原版
[code=JScri]
//要执行的循环次数
var time = 12,
iterations = Math.floor( time / 8 ),
startAt = time % 8; do{
switch(startAt){
case 0: alert('hello');
case 7: alert('hello');
case 6: alert('hello');
case 5: alert('hello');
case 4: alert('hello');
case 4: alert('hello');
case 3: alert('hello');
case 2: alert('hello');
}
startAt = 0;
}while(--iterations);
[/code]改进版
[code=JScri]
//要执行的循环次数
var time = 12,
i = time % 8; while(i){
alert(i--);
} i = Math.floor(time / 8); while(i){
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
alert(i--);
}
[/code]
上面的代码其实是我对书中的原本代码做了些改动,为了便于运行,但请放心我绝对没有改变算法的本质。
两段代码我觉得都有问题,但又不敢质疑权威。而且这本书提供别的代码都是正确的。网上搜索的duff's device的js版也都是这样写的。所以还是怀疑自己理解有问题。先看原版的。假如我需要调用alert函数12次。那么Math.floor(time)返回给iterations的值肯定是1。到底部的while(--iterations)的时候,直接就退出循环了,也就是说这个do{}while只会执行一次。那么调用到alert的次数是不对的,只有4次而已。再看改进版的。i = Math.floor(time / 8);i的值肯定也是1,那么下面的while(i)就会变成死循环了,因为js中0为fasle,非0为true,而i在循环体中每次调用alert(i--)会另i成为负数。就永远不会变为0了。两段代码存在的问题都无法执行成功,如果改一下就没问题了。
原版的将while(--iterations)改为while(iterations--)。
改进版的将下面的while(i)改为while(i>0)
不知各位理解了没,实在不明白到底是谁错了。
下面我在给出书中原本的代码:
原版
[code=JScri]
var iterations = Math.floor(items.length / 8),
startAt = items.length % 8,
i = 0; do{
switch(startAt){
case 0: process(items[i++]);
case 7: process(items[i++]);
case 6: process(items[i++]);
case 5: process(items[i++]);
case 4: process(items[i++]);
case 3: process(items[i++]);
case 2: process(items[i++]);
case 1: process(items[i++]);
}
startAt = 0;
}while(--iterations);
[/code]
改进版
[code=JScri]
var i = items.length % 8;
while(i){
process(items[i--]);
} i = Math.floor(items.length / 8); while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}
[/code]
就是用while/for把原始循环切大片,切成每8组一次循环,用switch case处理余下的循环次数.
var time = 12
iterations = Math.floor( (time+7) / 8 ), //这样才能正确执行while循环
startAt = time % 8;
do{
switch(startAt){
case 0: alert('hello world');
case 7: alert('hello world');
case 6: alert('hello world');
case 5: alert('hello world');
case 4: alert('hello world');
case 3: alert('hello world');
case 2: alert('hello world');
case 1: alert('hello world');
}
startAt = 0;
}while(--iterations);
'切成每8组一次循环'说错了,应是:切成每8次一组while循环
没有忽略掉,只是他原版取的是一个dom元素的集合数量作为循环次数,并且他在switch中调用的是一个process的函数。我为了可以更方便的运行,稍微改了下这里,不会影响他的算法逻辑。我所谓的原版,其实是一个叫jeff greenberg的人根据duff当年用在c上的duff's device改成js的版本。是他这个版本改的有问题。而duff用在c上的原版代码,是没有问题的。原版的用在c上的duff's device确实是将循环次数+7.其实我就是觉得他那个while(--iterations)这里似乎应该改为while(iterations--)就可以得到正确结果了。