2010-04-13 108 views
13

在Haskell中,有一个函数“take n list”,它返回列表中的前n个元素。例如,“sum(take 3 xs)”总结列表xs中的前三个元素。 F#有相同的吗?我认为它是列表功能之一,但我无法找到任何似乎匹配的东西。F#是否与Haskell相当?

回答

15

是的,它叫Seq.take。用法与Haskell的相似:Seq.take计数来源 要在列表中使用它,请先使用 List.toSeq(更新:显然是从评论,这是没有必要的。)

+0

这似乎正是我要找的。现在测试。 :) – McMuttons 2010-04-13 20:16:25

+13

其实,haskell的行为就像Seq.truncate,而不是Seq.take。 – sepp2k 2010-04-13 20:18:15

+2

由于列表继承自IEnumerable,因此您无需先将其转换为我能看到的序列。在列表中使用Seq.take运行良好。 – McMuttons 2010-04-13 20:23:51

39

为了澄清一些事情,Seq.takeSeq.truncate(如@ sepp2k指出)之间的不同之处在于第二个会给你一个序列返回最多您指定的元素的数量(但如果序列的长度较少,则会给您较少的元素)。

如果您尝试访问超出原始列表长度的元素(请注意,Seq.take函数因为结果是延迟生成的序列而没有立即抛出异常),所以生成的序列Seq.take会引发异常。

此外,您不需要明确地将列表转换为序列。在封面下,list<'a>是继承自seq<'a>类型的.NET类,它是一个接口。 seq<'a>类型实际上只是IEnumerable<'a>的一个类型别名,因此它由所有其他集合(包括数组,可变列表等)实现。下面的代码将正常工作:

let list = [ 1 .. 10 ] 
let res = list |> Seq.take 5 

但是,如果你想获得类型的结果list<int>你需要序列转换回列表(因为名单是更具体的类型不是顺序):

let resList = res |> List.ofSeq 

我不知道为什么F#库不提供List.takeList.truncate。我猜想目标是避免为所有类型的集合重新实现整套功能,所以那些在使用更具体的集合类型时序列实现足够好的应用程序仅在Seq模块中可用(但这只是我的猜测...)

+0

很好的说明。 – McMuttons 2010-04-13 21:14:07

+0

@McMuttons:谢谢!我不确定我是否应该发布它,因为大部分内容都已在评论中提及。所以,我很高兴它已被使用! – 2010-04-13 22:11:40

1

Seq.take的工作,正如其他人已经指出的那样,但是列表中的所有Seq操作都需要付出代价。就Seq.take而言,这并不奇怪,因为该列表必须复制。

更值得注意的是,例如,列表中的Seq.concat比List.concat需要更多的时间。 我想这意味着当你调用一个Seq.xxx函数时,你不会仅仅把它作为一个seq来访问,而且这个列表在后台也被复制/转换为一个Seq。

编辑:我画了上述结论的原因,是这样的替补使用F#互动:

#time "on";; 
let lists = [for i in 0..5000000 -> [i..i+1]];; 
Seq.length (Seq.concat lists);; 
List.length (List.concat lists);; 

在我的机器中,List.length版本大约需要1.9秒,而Seq.length版本大约需要3。8秒(仅限长度线重复测试的最短时间,不包括列表生成线)。

+3

我不认为这是正确的。 '列表<'t>'实现'seq <'t>'接口,所以不需要转换,也没有任何理由期望复制。此外,'Seq.take'懒惰地工作,而'List.take'不能,如果长列表中的序列操作几乎肯定会更快,如果只需要结果序列的前面。但是,通过模式匹配来实现List.take函数可能会比列举列表的Seq.take实现更好,如果您想要热切访问结果列表的所有元素。 – kvb 2011-09-14 14:00:30

+1

我对转换发生的结论可能是错误的,但有些事情正在进行,需要额外的时间。我编辑了我的帖子并添加了一些时间。 – Batibix 2011-09-25 10:44:35