鉴于:
open System.Linq
Run Code Online (Sandbox Code Playgroud)
这是一个可以接受的表达:
[2; 3; 4].SelectMany(fun n -> { 1..n })
Run Code Online (Sandbox Code Playgroud)
但这不是:
[2; 3; 4].SelectMany(fun n -> [ 1..n ])
Run Code Online (Sandbox Code Playgroud)
错误消息说:
int -> int list
is not compatible with type
int -> System.Collections.Generic.IEnumerable<'a>
Run Code Online (Sandbox Code Playgroud)
F#拒绝表达式,因为函数返回了一个int list.
考虑一下这个类似的C#程序:
using System;
using System.Collections.Generic;
using System.Linq;
namespace SelectManyCs
{
class Program
{
static List<int> Iota(int n)
{
var ls = new List<int>();
for (var i = 0; i < n; i++) ls.Add(i);
return ls;
}
static void Main(string[] args)
{
var seq = new List<int>() { 2, 3, 4 };
foreach (var elt in seq.SelectMany(Iota))
Console.WriteLine(elt);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Iota返回a List<int>并将其传递SelectMany给C#是可以接受的.
是设计的F#行为还是bug?如果是设计,为什么类似的操作在C#中工作?
这是预期的行为.C#通常在类型之间进行比F#更多的自动转换:
在C#中,lambda函数的结果是List<T>,但C#编译器自动将结果转换为IEnumerable<T>,这是函数的预期返回类型.
在F#中,编译器不会自动将结果转换为IEnumerable<T>,因此您的第二个代码段不会键入check - 因为它返回的list<T>是与预期不同的类型IEnumerable<T>(您可以将列表转换为可枚举,但它们是不同的类型).
F#库定义了自己的SelectMany调用操作版本Seq.collect.F#函数具有以下类型:
> Seq.collect;;
val it : (('a -> #seq<'c>) -> seq<'a> -> seq<'c>)
Run Code Online (Sandbox Code Playgroud)
这里,类型#seq<'c>明确表示结果可以是任何可以转换为的类型seq<'c>(这就是#名称中的含义).这就是为什么上一个问题的答案有效:
[2; 3; 4] |> Seq.collect (fun n -> [ 1..n ])
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1010 次 |
| 最近记录: |