雄辩的JavaScript,序列接口

雄辩的JavaScript,序列接口,javascript,Javascript,我正在努力学习有说服力的JavaScript,我对problem#6.3(第二版)有疑问 问题是: 设计一个接口,在一组 价值观提供此接口的对象表示一个序列, 接口必须以某种方式使使用 这样一个对象可以遍历序列,查看元素 它是由价值观组成的,并且有一些方法来确定何时结束 达到序列的最大值 指定接口后,尝试编写函数 获取序列对象并在其上调用console.log的logFive 前五个元素或更少,如果序列少于五个 元素 然后实现一个对象类型ArraySeq,它包装一个数组并允许 使用您设计的界面在

我正在努力学习有说服力的JavaScript,我对problem#6.3(第二版)有疑问

问题是:

设计一个接口,在一组 价值观提供此接口的对象表示一个序列, 接口必须以某种方式使使用 这样一个对象可以遍历序列,查看元素 它是由价值观组成的,并且有一些方法来确定何时结束 达到序列的最大值

指定接口后,尝试编写函数 获取序列对象并在其上调用console.log的logFive 前五个元素或更少,如果序列少于五个 元素

然后实现一个对象类型ArraySeq,它包装一个数组并允许 使用您设计的界面在阵列上进行迭代。实施 另一个对象类型RangeSeq,在一系列整数上迭代 (取而代之的是它的构造函数的from和to参数)

我写了这个解决方案:

function ArraySeq(collection) {
  this.values = collection;
}

ArraySeq.prototype.iterate = function(start, end, action) {
  var n = Math.min(this.values.length, end);
  for (var i = start; i < n; i++) {
    action(this.values[i]);
  }
};

function RangeSeq(from, to) {
  var array = [];
  for (var i = from; i <= to; i++)
    array.push(i);
  ArraySeq.call(this, array);
}

RangeSeq.prototype = Object.create(ArraySeq.prototype);

function logFive(sequenceObject) {
 sequenceObject.iterate(0, 5, console.log);
}

//This code to test how the solution works was provided by the author
logFive(new ArraySeq([1, 2]));
// → 1
// → 2
logFive(new RangeSeq(100, 1000));
// → 100
// → 101
// → 102
// → 103
// → 104
函数ArraySeq(集合){
这个值=集合;
}
ArraySeq.prototype.iterate=函数(开始、结束、操作){
var n=Math.min(this.values.length,end);
对于(变量i=开始;i=this.to)
返回false;
这个.pos++;
返回true;
};
RangeSeq.prototype.current=函数(){
返回此.pos;
};
logFive(新的ArraySeq([1,2]);
// → 1.
// → 2.
logFive(新范围seq(1001000));
// → 100
// → 101
// → 102
// → 103
// → 104
//此替代方法将空序列表示为null,
//并给出了非空序列的两种方法:
//
//*head()返回序列开头的元素。
// 
//*rest()返回序列的其余部分,如果没有,则返回null
//元素离开了。
//
//因为JavaScript构造函数不能返回null,所以我们添加了make
//函数调用此类型序列的构造函数,其中
//一个序列,如果结果序列是
//空的。
功能日志Five2(顺序){
对于(变量i=0;i<5&&sequence!=null;i++){
console.log(sequence.head());
sequence=sequence.rest();
}
}
函数ArraySeq2(数组,偏移){
this.array=数组;
这个偏移量=偏移量;
}
ArraySeq2.prototype.rest=函数(){
返回ArraySeq2.make(this.array,this.offset+1);
};
ArraySeq2.prototype.head=函数(){
返回此.array[this.offset];
};
ArraySeq2.make=函数(数组,偏移){
如果(offset==null)offset=0;
if(偏移量>=array.length)
返回null;
其他的
返回新的ArraySeq2(数组,偏移);
};
功能范围Seq2(从,到){
this.from=from;
这个;
}
RangeSeq2.prototype.rest=函数(){
返回范围seq2.make(this.from+1,this.to);
};
RangeSeq2.prototype.head=函数(){
把这个还给我;
};
RangeSeq2.make=函数(从,到){
如果(从>到)
返回null;
其他的
返回新的RangeSeq2(从,到);
};
logFive2(ArraySeq2.make([1,2]);
// → 1.
// → 2.
logFive2(范围seq2.make(1001000));
// → 100
// → 101
// → 102
// → 103
// → 104
我的问题是:

  • 我的解决方案产生的结果与作者的解决方案完全相同。我用不同的测试用例测试了这三个。在编写我的解决方案时,我确实利用了本章的材料(本章讨论JavaScript中的OOP技术、继承等)考虑到所有这些,我的解决方案是否错误?

  • 为什么他的解决方案是这样写的?我是说,我的方法有问题吗?不够抽象?我的方法接受参数是件坏事吗?他的方法不需要论证与他的解决方案相比,我的解决方案有什么缺点吗?(我必须承认,我几乎不理解这个问题,所以我的解决方案可能反映了我对这个问题的理解水平。)


  • 谢谢大家!

    您的解决方案是不正确的,因为迭代器协议的全部要点是惰性地迭代,因此
    RangeSeq(1100000)
    不会用所有元素填满内存,如果我们只需要五个元素

    作者的代码还可以,但不必要的冗长。迭代器协议通常是这样实现的:

    • Iterable是具有
      迭代器()
      方法的对象
    • Iterable.iterator()
      返回迭代器对象
    • 迭代器提供方法
      .next
      ,该方法返回一对
      (完成,值)
      。或者,
      .next
      可以在对耗尽的迭代器调用时引发异常
    示例:

    函数范围seq(从,到){
    this.iterator=函数(){
    var i=来自;
    返回{
    下一步:函数(){return[i>=to,i++]}
    }
    }
    }
    函数ArraySeq(ary){
    this.iterator=函数(){
    var i=0;
    返回{
    下一步:函数(){return[i>=ari.length,ari[i++]}
    }
    }
    }
    功能日志(序号,n){
    var it=seq.iterator();
    而(n--){
    让[done,value]=it.next();
    如果(完成)中断;
    console.log(值)
    }
    }
    logN(新范围seq(100100000000),5);
    console.log('------------');
    logN(新ArraySeq([11,22,33,44,55,66,77,88,99]),5)模式“return{next:function(){…}}”对我来说是新的。书中有一些类似的东西,比如“get:function(){…}”和“set:function(){…}”,但有点简单。这可能就是为什么他的解决方案冗长的原因——他只能依赖于他介绍的东西
    
    // I am going to use a system where a sequence object has two methods:
    //
    // * next(), which returns a boolean indicating whether there are more
    //   elements in the sequence, and moves it forward to the next
    //   element when there are.
    //
    // * current(), which returns the current element, and should only be
    //   called after next() has returned true at least once.
    
    function logFive(sequence) {
      for (var i = 0; i < 5; i++) {
        if (!sequence.next())
          break;
        console.log(sequence.current());
      }
    }
    
    function ArraySeq(array) {
      this.pos = -1;
      this.array = array;
    }
    ArraySeq.prototype.next = function() {
      if (this.pos >= this.array.length-1)
        return false;
      this.pos++;
      return true;
    };
    ArraySeq.prototype.current = function() {
      return this.array[this.pos];
    };
    
    function RangeSeq(from, to) {
      this.pos = from - 1;
      this.to = to;
    }
    RangeSeq.prototype.next = function() {
      if (this.pos >= this.to)
        return false;
      this.pos++;
      return true;
    };
    RangeSeq.prototype.current = function() {
      return this.pos;
    };
    
    logFive(new ArraySeq([1, 2]));
    // → 1
    // → 2
    logFive(new RangeSeq(100, 1000));
    // → 100
    // → 101
    // → 102
    // → 103
    // → 104
    
    // This alternative approach represents the empty sequence as null,
    // and gives non-empty sequences two methods:
    //
    // * head() returns the element at the start of the sequence.
    // 
    // * rest() returns the rest of the sequence, or null if there are no
    //   elemements left.
    //
    // Because a JavaScript constructor can not return null, we add a make
    // function to constructors of this type of sequence, which constructs
    // a sequence, or returns null if the resulting sequence would be
    // empty.
    
    function logFive2(sequence) {
      for (var i = 0; i < 5 && sequence != null; i++) {
        console.log(sequence.head());
        sequence = sequence.rest();
      }
    }
    
    function ArraySeq2(array, offset) {
      this.array = array;
      this.offset = offset;
    }
    ArraySeq2.prototype.rest = function() {
      return ArraySeq2.make(this.array, this.offset + 1);
    };
    ArraySeq2.prototype.head = function() {
      return this.array[this.offset];
    };
    ArraySeq2.make = function(array, offset) {
      if (offset == null) offset = 0;
      if (offset >= array.length)
        return null;
      else
        return new ArraySeq2(array, offset);
    };
    
    function RangeSeq2(from, to) {
      this.from = from;
      this.to = to;
    }
    RangeSeq2.prototype.rest = function() {
      return RangeSeq2.make(this.from + 1, this.to);
    };
    RangeSeq2.prototype.head = function() {
      return this.from;
    };
    RangeSeq2.make = function(from, to) {
      if (from > to)
        return null;
      else
        return new RangeSeq2(from, to);
    };
    
    logFive2(ArraySeq2.make([1, 2]));
    // → 1
    // → 2
    logFive2(RangeSeq2.make(100, 1000));
    // → 100
    // → 101
    // → 102
    // → 103
    // → 104