2010-04-13 141 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