2016-03-03 35 views
2

如果我有一個名爲的人如何撰寫功能列表?

let checks = [checkAge; checkWeight; checkHeight] 

一個類型和功能列表,例如......每個函數的類型(人 - >布爾),我想要做的相當於...

checkAge >> checkWeight >> checkHeight 

......但我不知道在列表中有什麼功能,我該怎麼做呢?

我嘗試以下...

checks |> List.reduce (>>) 

...但是這提供了以下錯誤......

錯誤FS0001:類型不匹配。期待一個 (人 - > BOOL) - >(人 - > BOOL) - >人 - > BOOL

但給予

(人 - > BOOL) - >(布爾 - >「一) - >人物 - >「一個

類型‘人’不匹配‘BOOL’類型

我在做什麼錯?

+1

要使用'>>'運算符,右側的函數必須將左側的結果類型作爲其參數。 'checkAge'返回'bool',但'checkWeight'期待'Person'。你想要最終的結果意味着什麼? – TeaDrivenDev

+0

@TeaDrivenDev我想結合這些函數,所以我可以一次調用結果,而不是每次循環一次函數。這一切始於我試圖改進此F#片段頁面上的代碼... http://www.fssnip.net/7h –

回答

4

你可能莫名其妙地在一個可怕的偶然概念。它的功能編程Voldemort(不要說他的名字!)。

由於沒有進一步的ADO讓步行進入代碼:

type Person = 
    { Name : string 
     Age : int 
     Weight : int 
     Height : int } 

type Result = 
    | Ok of Person 
    | Fail 

let bind f m = 
    match m with 
    | Ok p -> f p 
    | _ -> Fail 

let (>=>) f1 f2 = f1 >> (bind f2) 

let checkAge p = 
    if p.Age > 18 then Ok(p) 
    else Fail 

let checkWeight p = 
    if p.Weight < 80 then Ok(p) 
    else Fail 

let checkHeight p = 
    if p.Height > 150 then Ok(p) 
    else Fail 

let checks = [ checkAge; checkWeight; checkHeight ] 
let allcheckfunc = checks |> List.reduce (>=>) 

let combinedChecks = 
    checkAge 
    >=> checkWeight 
    >=> checkHeight 


let p1 = 
    { Name = "p1" 
     Age = 10 
     Weight = 20 
     Height = 110 } 

let p2 = 
    { Name = "p2" 
     Age = 19 
     Weight = 65 
     Height = 180 } 

allcheckfunc p1 
combinedChecks p1 

allcheckfunc p2 
combineChecks p2 

在這一點上我可以扔開了不少怪人隱語(不是真的,我不能......),還是讓我們只是看在我所做的事上。

  • 我放棄布爾你的返回值,並與要麼去了另一種類型(結果)(標記關鍵字!)失敗

  • 然後由從結果型的輔助(綁定)來包裝和unwrapp東西。

  • 和一個新的操作符(> =>)結合reduce中的東西。

注意,第一單向函數來故障將快捷整個鏈和或多或少立即(不調用其他功能)返回故障。另外你不會知道在這條鏈上它做了什麼失敗或哪個功能提前失敗確實是確定

有方法,你走,讓你獲得類型的反饋也積累了錯誤:「該checkAge返回失敗,但其他人是great success"

的代碼大多是從被盜在這裏:http://fsharpforfunandprofit.com/posts/recipe-part2/

而且你可能想了解Wlaschin的整個網站甚至更多,如果想進入更細,更難細節

在您的旅途好運來的樓上Ivory Tower。 ;-)

腳註:這通常稱爲Either-monad。它沒有完全完成,什麼不在上面的代碼,但沒關係......我認爲它會在你的情況下工作...

+0

感謝您的好解釋!正如我對Brunner評論的那樣,我在玩這個片段時想到了ROP,看起來你正在做的事情是相關的。我沒有花足夠的時間用你的代碼來充分理解它,但我可以看到你要去的地方。我需要更多地看待他們,並決定以誰作爲答案。我希望我可以標記他們兩個! –

+0

平心而論,我認爲他們是一樣的。 OTOH之所以選擇我的近似就是爲了理解這一點。我可以使用與Brunner完全相同的技術,而不是使用> =>運算符。這是我可以不使用的一個區別,並使用與Brunner相同的(>>)。這兩個答案中的另一個主要區別是返回的內容。 Mine使用了OK/Fail,但如果仔細觀察它,Some/None完全映射到我的結構,其餘代碼(除了> =>之外)也是如此。所以這是相同的(提示抗議),雖然他們有不同的名字(也許vs兩者)。 –

+0

他們幾乎一樣,同意。畢竟,我們引用的是同一個源代碼。我唯一的抗議就是我只用我提供的簡單/簡單的方式使用前向合成操作符。除了操作員的註釋之外,我們的答案也是一樣的,包括結果。我覺得你應該被標記爲答案,畢竟你比我快。 (我不喜歡急於回答問題,僅僅發表了幾句話,只是讓它在那裏 - 但那不是你所做的)。 – Brunner

4

如果您有執行某種轉換的函數,則>>運算符很有用。例如,如果您有一個功能Person -> Person的列表,可將一個人變成另一個人。

在你的情況,似乎你有功能Person -> bool,你想建立一個組合功能,返回true,如果所有功能返回true

使用List.reduce你可以寫:

checks|> List.reduce (fun f g -> (fun p -> f p && g p)) 

也許是一個更簡單的辦法是隻寫一個函數,它接受一個人,並使用List.forall

let checkAll checks person = checks |> List.forall (fun f -> f person) 
+0

感謝您的答覆。實際上,我試圖通過編寫一個函數列表,將函數列表中的函數概括爲http://www.fssnip.net/7h,然後將它們組合起來並運行組合結果。該片段手動組合了這些功能,因此它特定於所討論的問題。我可以這樣做嗎? –

4

它看起來像Railway oriented programming將是一個很好的適合在這裏。 如果你選擇走這條路線,你基本上有兩個選擇。 您可以全部進入或快速路線。

快速航線

你重寫你的驗證功能,採取Person option,而不是隻是普通Person

let validAge (record:Record option) = 
     match record with 
     | Some rec when rec.Age < 65 && rec.Age > 18 -> record 
     | None -> None 

現在你應該可以很容易地鏈接你的功能。

checks |> List.reduce (>>) 

所有

另外,如果你是懶惰,不想匹配..在每個驗證功能,你可以寫更多的代碼。 (取自[1]的樣本)

首先有一些設置要做。 我們將定義一個特殊的返回類型,這樣我們可以得到有意義的錯誤消息。

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess 
    | Failure of 'TFailure 

綁定功能,在驗證綁定在一起

let bind switchFunction = 
    function 
    | Success s -> switchFunction s 
    | Failure f -> Failure f 

你必須重寫您的驗證功能,以及。

let validAge (record:Record) = 
    if record.Age < 65 && record.Age > 18 then Success input 
    else Failure "Not the right age bracket" 

現在用

checks |> List.reduce (fun acc elem -> acc >> bind elem) 

無論哪種方式相結合,檢查出的原創文章。 還有更多你可能可以使用:)

編輯:我只是注意到,我再次寫這個答案太慢了。 此外,我認爲赫爾傑比我更好地解釋了這個概念。

+0

也許......但我也是。 ;-) –

+0

@Brunner我剛剛回來更新我的問題,並添加我的答案,這與你的和Helge的基本相同。是的,在看片段時,它看起來像ROP,我試圖應用這個原則。我結束的代碼與你的代碼略有不同(我沒有使用DU),但我認爲原理是一樣的。現在我必須決定h =誰的回答標記爲接受!他們都很優秀 –