zoukankan      html  css  js  c++  java
  • 使用JavaScript ES6的新特性计算Fibonacci(非波拉契数列)

    程序员面试系列

    Java面试系列-webapp文件夹和WebContent文件夹的区别?

    程序员面试系列:Spring MVC能响应HTTP请求的原因?

    Java程序员面试系列-什么是Java Marker Interface(标记接口)

    使用JDK自带的工具jstack找出造成运行程序死锁的原因

    编程面试题:编写一个会造成数据库死锁的应用

    JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载

    面试题:用JavaScript开发一个函数,打印非波拉契数列。

    我们只要记住非波拉契数列的计算公式,就不难写出来了:

    F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)

    我写的JavaScript代码如下:

    
    var fib = function (a, b) {
    
       var _current = a + b;
    
       return {
    
           current: _current,
     
           next: function () {
    
                return fib(b, _current);
    
            }
      }
    }
    

    把当前这一轮的计算结果存储到第二行的变量_current里,并通过属性current返回给调用者。返回的json对象除了current属性外,还有另一个属性next,指向一个闭包函数调用。一旦next指向的函数再次被调用,则会再次触发数列的计算。

    var generator = fib(1,1);
    
    // 前一行调用fib(1,1)计算1+1的结果为2,将2存储到_current里通过current属性返回,所以打印2
    
    // 同时返回next函数,函数体为return fib(b, _current); 此时b为1,_current为2
    
    console.log(generator.current);
    
    // 一旦执行next函数,则执行其指向的return fib(b, _current); 1 + 2 = 3
    
    var result = generator.next();
    
    console.log(result.current); // 打印3
    
    

    如果要打印10个非波拉契数列的值,意味着我要重复调用9次fib函数,太麻烦。于是我写了个函数把fib调用包裹起来。

    这个包裹函数有两个输入参数,n为希望生成非波拉契数列元素的个数,第二个参数sequence接受一个函数。

    var take = function(n, sequence) {
    
        var result = [];
    
        var temp = sequence;
    
        for (var i = 0; i < n; i++) {
    
             result.push(temp.current);
    
             temp = temp.next();
    
        }
    
       return result;
    
    }
    

    现在我只需要一行语句,就能打印10个非波拉契数列的元素出来。

    console.log(take(10, fib(1,1)));

    采用ES6的GeneratorFunction生成非波拉契数列

    ES6提供了原生GeneratorFunction的支持,语法非常有特色,关键字function后面紧跟一个星号。GeneratorFunction的详细介绍参考官网:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

    先看如何用GeneratorFunction这个黑科技重新实现非波拉契树立的生成。代码如下:

    var fib_generator = function *(){
    
         var current = 0, next = 1;
    
          while(true) {
    
              [next, current] = [next+current, next];
    
              yield current;
    
         }
    
    }
    
    var fib = fib_generator();
    
    for(var i = 0; i < 10; i++){
    
          console.log(fib.next().value);
    
    }
    

    第5行从语义上非常清晰地体现出了非波拉契数列的计算公式:

    F(n)=F(n-1)+F(n-2)

    然而它是包含在一个while(true)的无限循环内的,所以这段代码是如何工作的呢?

    最好的学习办法就是单步调试。

    代码第40行到第47行,我们使用了ES6 function*关键字得到了一个"function generator"。在这个generator内部,我们定义了一个无限循环,用于计算非波拉契数列。

    第49行,我们用()调用了这个generator,将结果存储在变量fib里。直到此时,function generator的实现体,即代码41~45行还没有得到执行。

    实际上,49行的变量lib只是维护了一个指向fib_generator的ITERATOR指针。

    这个ITERATOR自带了一个名为next的方法,是ES6的原生实现,大家看上图调试器里的fib.next显示的是native code。Functiongenerator的神奇之处在于,当next方法被调用一次,则generator内部的函数体也只会执行一次。

    单步执行,执行一次next方法:

    注意调用栈,此时我们已经进入fib_generator函数体内部了:

    一旦在FunctionGenerator实现体内部执行到yield关键字,则当前计算结果作为返回值返回给consumer。也就是说,一旦执行遇到yield,则自动从无限循环中退出。

    下列简单的循环会打印10个非波拉契数列的元素:

    for(var i = 0; i < 10; i++){
    
         var currentResult = fib.next();
    
          console.log(currentResult.value);
    
    }
    

    从上面的代码能看出,yield关键字返回一个json对象给消费者,该对象有个名为name的属性,包含的是具体计算的数值。Json对象的另一个属性名为done,类型为boolean,意思是这个FunctionGenerator的函数体执行是否已经结束。在我的这个例子里,每次next调用的yield返回的Json对象的done属性都为false,因为我的FunctionGenerator内部是一个无限循环。

    采用ES6的FunctionGenerator打印出的结果和常规写法一致。

    相信您面试的时候,如果能用ES6的FunctionGenerator完成这道题目,一定能让面试官对您刮目相看。

    我写的程序员面试系列

    Java面试系列-webapp文件夹和WebContent文件夹的区别?

    程序员面试系列:Spring MVC能响应HTTP请求的原因?

    Java程序员面试系列-什么是Java Marker Interface(标记接口)

    使用JDK自带的工具jstack找出造成运行程序死锁的原因

    编程面试题:编写一个会造成数据库死锁的应用

    JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载

    要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

  • 相关阅读:
    程序中图片透明 函数(使用SetBkColor API函数)
    编程中使用锁
    event内存泄漏
    diskcache
    linux内核管理
    Vue
    Paxos算法
    索引以及页和区
    CoreRT
    二叉树
  • 原文地址:https://www.cnblogs.com/sap-jerry/p/9818915.html
Copyright © 2011-2022 走看看