2011-10-10 51 views
13

比方說,我有一個列表和一組,我想要做的一些東西與他們:F#:在地圖殺死冗餘/縮小/濾波器

let outA = inA |> List.map(fun x -> x + 1) |> List.filter(fun x -> x > 10) 
let outB = inB |> Set.map(fun x -> x + 1) |> Set.filter(fun x -> x > 10) 

現在,顯然是處理名單和B處理組。然而,我覺得非常煩人的是不得不一遍又一遍地寫List.List.:它不僅是冗長的,重複的樣板,它沒有傳達任何信息,並妨礙了閱讀代碼的功能,它也是事實上的類型註釋,我必須跟蹤並與代碼的其餘部分保持同步。

我希望能夠做的是類似如下:

let outA = inA |> map(fun x -> x + 1) |> filter(fun x -> x > 10) 
let outB = inB |> map(fun x -> x + 1) |> filter(fun x -> x > 10) 

隨着編譯器知道INA是列表,INB是一組,因此,所有的操作都是從正確的類,因此outA是一個列表,outB是一個集合。我可以通過Seq部分實現這一點:

let map(x) = 
    Seq.map(x) 

let filter(x) = 
    Seq.filter(x) 

而且我可以寫得完全一樣。問題在於它將所有內容都轉換爲序列,並且我不能再對它們執行列表/設置操作。同樣,

let outA = inA.Select(fun x -> x + 1).Where(fun x -> x > 10) 
let outB = inB.Select(fun x -> x + 1).Where(fun x -> x > 10) 

也刪除所有,但然後將所有東西都轉換爲IEnumerable。我設法得到它相當不錯的擴展通過將所有的靜態方法爲實例方法:

type Microsoft.FSharp.Collections.List<'a> with 
    member this.map(f) = this |> List.map(f) 
    member this.filter(f) = this |> List.filter(f) 

let b = a.map(fun x -> x + 1).filter(fun x -> x > 10) 

但我懷疑這會碰上這裏所提到的類型推斷的問題:Method Chaining vs |> Pipe Operator?我真的不知道;我不熟悉類型推斷算法的工作原理。

底線是,我想我會做很多這些列表/設置/數組映射/減少/過濾操作,我想讓他們看起來儘可能漂亮和乾淨。現在,除了分散我在表達式中的重要位置(即「map」和lambda)外,他們還提供了事實上的類型註釋,在編譯器應該快速的地方 - 很好地知道我的集合是什麼傳入是。此外,這正是OO繼承和多態性意味着要解決的事情,所以我想知道是否有一些等價的功能模式,我不知道它會像優雅地解決它。也許我可以在我自己的靜態map中進行類型檢查,並在輸入上執行相關類型的map函數?

有沒有人比我已經嘗試過的更好的解決方案,還是我碰到F#類型推理引擎的根本限制,我應該接受並繼續前進?

+2

在我看來,在模塊名稱使代碼更具描述性。通過查看代碼,您知道它是一個List/Set。 – ebb

+4

基本上你想要type-classes :)結帳Haskell,或者留在我們這裏並使用合格的名字。 –

+1

@ebb:但是類型推理的觀點是,我不需要描述這麼多的代碼= D。 –

回答

11

這不是OO /繼承/多態性解決的問題。而是'類型'(la Haskell)解決的問題。 .NET類型系統不能執行類型類型,而F#不會嘗試添加該類型的功能。 (你可以用inline做一些毛茸茸的技巧,但它有各種限制。)

我建議接受它並繼續前進。

+0

我正在研究類型類。我認爲OO/Inheritance/Polymorphism的意義在於你可以給它一個方法名稱,並且它會立即知道(根據你放入什麼樣的「this」)*調用哪個方法(從多個類中調用哪一個都有這個方法),這是我的問題。還是我完全錯過了一些好評? –

+3

在較高的層面上,您的描述是正確的,而且OO似乎會這樣做。但是在細節上,具有子類型的類型不能解決......請檢查類型類,但也可能會看到「方差」(協方差和相反方向),因爲它與面向對象有關,以便了解面向對象的原因一般來說很好解決。 – Brian

+2

+1,但我認爲對類型類的支持很可能在.NET上完成,只是F#的類型系統沒有實現這一點。 – Ingo

5

我同意布萊恩 - 你會習慣這個。正如我在上一個問題的答案中所提到的,這有時非常有用,因爲您會看到代碼的作用(哪部分懶惰地評估,哪部分使用數組提高效率等等)。)

此外,某些功能僅適用於某些類型 - 例如有List.tailList.foldBack,但類似的功能沒有爲IEnumerable存在(在Seq模塊) - 有很好的理由,因爲它們會導致糟糕的代碼。

在Haskell中,類型類可以描述具有某些函數的類型(如mapfilter),但我認爲它們不能很好地縮放 - 爲F#庫指定類型類,最終會得到一個層次結構類型類,它們指定類似列表的數據結構,類似列表的數據結構,類似於tailfoldBack類似的函數以及太多的其他約束。

除此之外,對於許多簡單的列表處理的工作,你也可以使用內涵,給你要好很多語法(當然,這是唯一有用的基本的東西):

let outB = seq { for x in inA do 
        if x > 10 do yield x + 1 }