最近在看《高性能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]

解决方案 »

  1.   

    是不是原版有什么处理被你忽略掉了.
    就是用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); 
      

  2.   

    sorry
    '切成每8组一次循环'说错了,应是:切成每8次一组while循环
      

  3.   


    没有忽略掉,只是他原版取的是一个dom元素的集合数量作为循环次数,并且他在switch中调用的是一个process的函数。我为了可以更方便的运行,稍微改了下这里,不会影响他的算法逻辑。我所谓的原版,其实是一个叫jeff greenberg的人根据duff当年用在c上的duff's device改成js的版本。是他这个版本改的有问题。而duff用在c上的原版代码,是没有问题的。原版的用在c上的duff's device确实是将循环次数+7.其实我就是觉得他那个while(--iterations)这里似乎应该改为while(iterations--)就可以得到正确结果了。