F# 什么';s是seq的真实类型<;T>;?

F# 什么';s是seq的真实类型<;T>;?,f#,F#,seq是IEnumerable接口的别名,您可以创建实现IEnumerable的对象并使用其方法,例如: IEnumerable<int> list = new List<int> { 1, 2, 3, 4, 5 }; 但必须实例化一个实现IEnumerable的对象 但是。。。在F#中,您可以创建如下序列: let list = seq { for i in 1..5 -> i } VisualStudio说您的列表具有seq类型。这是不可能的,seq是一个接

seq是IEnumerable接口的别名,您可以创建实现IEnumerable的对象并使用其方法,例如:

IEnumerable<int> list = new List<int> { 1, 2, 3, 4, 5 };
但必须实例化一个实现IEnumerable的对象

但是。。。在F#中,您可以创建如下序列:

let list = seq { for i in 1..5 -> i }
VisualStudio说您的列表具有seq类型。这是不可能的,seq是一个接口(IEnumerable)并且您不能创建接口的实例

那么,seq里面的魔力是什么

在FSI中使用GetType:

let goingToSee = seq { for i in 1..5 -> i }
goingToSee.GetType();;
val goingToSee : seq<int>
val it : System.Type = FSI_0010+goingToSee@12
let goingToSee=seq{for i in 1..5->i}
goingToSee.GetType();;
val goingToSee:seq
val it:System.Type=FSI_0010+goingToSee@12

F#中的
seq{..}
表达式更类似于C#中的迭代器方法(使用
yield
关键字编写),而不是集合初始值设定项。与C#编译器对迭代器的处理类似,F#编译器将
seq{..}
表达式转换为实现
IEnumerable
的类

编译后的类继承自
GeneratedSequenceBase
(请参见),并将根据您在序列表达式中编写的内容生成的代码放入。它被编译为一个状态机,因此代码看起来有点难看,但是如果您使用ILSpy查看它,它看起来像这样:

internal sealed class list@6 : GeneratedSequenceBase<int> {
  public override int GenerateNext(ref IEnumerable<int> next) {
      switch (this.pc) {
          case 1: goto IL_82;
          case 2: this.i = 0; break;
          case 3: goto IL_A3;
          default: {
              this.@enum = Operators.OperatorIntrinsics.RangeInt32(1, 1, 5).GetEnumerator();
              this.pc = 1;
              break; }
    }
      if (this.@enum.MoveNext()) {
          this.i = this.@enum.Current;
          this.pc = 2;
          this.current = this.i;
          return 1;
      }
      IL_82:
        this.pc = 3;
        LanguagePrimitives.IntrinsicFunctions.Dispose<IEnumerator<int>>(this.@enum);
      this.@enum = null;
        this.pc = 3;
      IL_A3:
        this.current = 0;
        return 0;
  }
}
内部密封类list@6:GeneratedSequenceBase{
公共覆盖int GenerateNext(参考IEnumerable next){
开关(此.pc){
案例1:goto IL_82;
案例2:this.i=0;中断;
案例3:goto IL_A3;
默认值:{
这是@enum=Operators.OperatorIntrinsics.RangeInt32(1,1,5).GetEnumerator();
这1.pc=1;
中断;}
}
if(这是@enum.MoveNext()){
this.i=this.@enum.Current;
这1.pc=2;
this.current=this.i;
返回1;
}
IL_82:
这1.pc=3;
LanguagePrimitives.IntrinsicFunctions.Dispose(此@enum);
这是@enum=null;
这1.pc=3;
IL_A3:
该电流=0;
返回0;
}
}
我不会尝试解码这个,但我认为
pc
保持状态机的状态。根据这一点,它要么初始化迭代器,移动到下一个状态,要么处理可能使用的任何资源


还值得注意的是,名称
list@6
是生成的类的行号。

list.GetType()告诉您什么?@HonzaBrestan非常奇怪。我已经更新了帖子。我不知道F#编译器的内部结构,但我推测编译器会创建一个名为
fsi0010的新类型+goingToSee@12
它实现了
IEnumerable
。它的
GetEnumerator
方法将返回一个自定义
IEnumerator
,该方法将包含基于代码中的
seq{…}
主体惰性构建序列的逻辑。这将使它不同于自定义计算表达式生成器,后者必须操作特定但通用的类型表示。。。也许我完全错了,可能有人知道的更多,让我们先等有人回答了再把这篇文章作为回答,但从最实际的角度来看,具体的实现应该无关紧要。这是一个惰性序列,它实现了
IEnumerable
,并按照您的预期运行。它类似于C#/LINQ方法,比如
Enumerable.Range(1,5)
——它提供了一些
IEnumerable
,您的代码不应该关心它的具体类型是什么,只要它的行为符合合同而不是f#guy,但您可以查看哪些可能有助于检查内部。
internal sealed class list@6 : GeneratedSequenceBase<int> {
  public override int GenerateNext(ref IEnumerable<int> next) {
      switch (this.pc) {
          case 1: goto IL_82;
          case 2: this.i = 0; break;
          case 3: goto IL_A3;
          default: {
              this.@enum = Operators.OperatorIntrinsics.RangeInt32(1, 1, 5).GetEnumerator();
              this.pc = 1;
              break; }
    }
      if (this.@enum.MoveNext()) {
          this.i = this.@enum.Current;
          this.pc = 2;
          this.current = this.i;
          return 1;
      }
      IL_82:
        this.pc = 3;
        LanguagePrimitives.IntrinsicFunctions.Dispose<IEnumerator<int>>(this.@enum);
      this.@enum = null;
        this.pc = 3;
      IL_A3:
        this.current = 0;
        return 0;
  }
}