给定:
open System.Linq
这是一个可接受的表达方式:
[2; 3; 4].SelectMany(fun n -> { 1..n })
然而这不是:
[2; 3; 4].SelectMany(fun n -> [ 1..n ])
错误信息是这样的:
int -> int list
is not compatible with type
int -> System.Collections.Generic.IEnumerable<'a>
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);
}
}
}
Iota
返回 List<int>
并将其传递给 SelectMany
C# 可以接受。
F# 行为是设计使然还是错误?如果是设计使然,为什么类似的操作在 C# 中有效?
最佳答案
这是预期的行为。 C# 通常比 F# 做更多类型间的自动转换:
在 C# 中,lambda 函数的结果是
List<T>
, 但 C# 编译器会自动将结果转换为IEnumerable<T>
,这是函数的预期返回类型。在 F# 中,编译器不会自动将结果转换为
IEnumerable<T>
所以你的第二个片段没有输入检查 - 因为它正在返回list<T>
这与预期的类型不同IEnumerable<T>
(您可以将列表转换为可枚举的,但它们是不同的类型)。
F# 库定义了自己的 SelectMany
版本操作称为 Seq.collect
. F# 函数具有以下类型:
> Seq.collect;;
val it : (('a -> #seq<'c>) -> seq<'a> -> seq<'c>)
在这里,输入 #seq<'c>
明确表示结果可以是任何可以转换为 seq<'c>
的类型(这就是名称中的 #
的意思)。这就是为什么 answer to your previous question作品:
[2; 3; 4] |> Seq.collect (fun n -> [ 1..n ])
关于c# - int -> int 列表与类型 int -> IEnumerable<'a> 不兼容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23316324/