在Haskell中,有一个函数“take n list”,它返回列表中的前n个元素。例如,“sum(take 3 xs)”总结列表xs中的前三个元素。 F#有相同的吗?我认为它是列表功能之一,但我无法找到任何似乎匹配的东西。F#是否与Haskell相当?
回答
是的,它叫Seq.take
。用法与Haskell的相似:Seq.take
计数来源。
要在列表中使用它,请先使用
(更新:显然是从评论,这是没有必要的。)List.toSeq
。
为了澄清一些事情,Seq.take
和Seq.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.take
或List.truncate
。我猜想目标是避免为所有类型的集合重新实现整套功能,所以那些在使用更具体的集合类型时序列实现足够好的应用程序仅在Seq
模块中可用(但这只是我的猜测...)
很好的说明。 – McMuttons 2010-04-13 21:14:07
@McMuttons:谢谢!我不确定我是否应该发布它,因为大部分内容都已在评论中提及。所以,我很高兴它已被使用! – 2010-04-13 22:11:40
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秒(仅限长度线重复测试的最短时间,不包括列表生成线)。
这似乎正是我要找的。现在测试。 :) – McMuttons 2010-04-13 20:16:25
其实,haskell的行为就像Seq.truncate,而不是Seq.take。 – sepp2k 2010-04-13 20:18:15
由于列表继承自IEnumerable,因此您无需先将其转换为我能看到的序列。在列表中使用Seq.take运行良好。 – McMuttons 2010-04-13 20:23:51