2016-09-29 72 views
3

在搜索答案時,我發現了幾個與我的情況不太匹配的問題 - 所以我會問一個新問題。在F中輕鬆獲取函數名稱#

我正在爲一個數據結構寫一些FsCheck測試,我想在這個數據結構的每個副本上檢查大約二十個不同的屬性。我到目前爲止已經做的是編寫每個屬性的謂詞,那麼我所做的謂詞的列表,我會用List.forall打電話給他們每個反過來,像這樣:

module PropertyChecks = 
    let ``Tail length plus tree length should equal vector length`` vec = 
     treeLength vec.root + Array.length vec.tail = vec.len 

    let ``The tail may only be empty iff the vector is empty`` vec = 
     (vec.len = 0) = (Array.length vec.tail = 0) 

    let ``The tail's length may not exceed tailMax`` vec = 
     Array.length vec.tail < tailMax 

    let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec = 
     (vec.len > tailMax) || (Array.length vec.root = 0) 

    // And so on for about 15 more predicates 

    let allProperties = 
     [ 
     ``Tail length plus tree length should equal vector length`` 
     ``The tail may only be empty iff the vector is empty`` 
     ``The tail's length may not exceed tailMax`` 
     ``If vec.len <= tailMax, all items are in tail and root is empty`` 
     ] 

    let checkProperties vec = 
     allProperties |> List.forall (fun pred -> pred vec) 

    // Rest of my code omitted since it's not relevant to the question 

問題我面對的是,我期望當一個屬性失敗,因爲我沒有正確構建數據結構時,兩個或三個其他屬性將同時失敗。我想列出所有失敗的屬性,這意味着在checkProperties中,我想提取失敗謂詞的名稱。我已經看到了多條答案:「你不能從你接收到的作爲參數的任意F#函數中獲得MethodInfo,因爲你永遠不知道你是否獲得了函數本身,或lambda或匿名函數」 。但在這裏,我不僅知道我有真正的功能,我知道他們的名字是什麼。我可以很容易地將他們的名字複製並粘貼到字符串中,並且使得(字符串,函數)元組列表成爲allProperties。但是我已經對複製粘貼一次(將謂詞放入列表中)感到不滿意,我寧願不做兩次。

什麼是更好的解決方案,使(函數名稱,函數)元組列表?我可以將allPropertiescheckProperties移出PropertyChecks模塊,以便它只包含謂詞,然後使用反射來遍歷該模塊?

我對整個.Net反射系統很陌生,所以我可能會錯過一些明顯的東西。請隨時指出,如果我錯過了它,我不會感到侮辱。

+1

在沒有與反思一個棘手的溶液中,也不會太硬解析與水平的shell腳本文件,你需要 –

+0

我讀的標題和只能認爲:lambda,lambda,lambda。 –

回答

2

這裏是一個哈克bash腳本,可能比反射更容易:

#!/bin/bash 
echo "let pairs = [|"; 
cat t.fs | grep let | grep -o "\`\`[^\`]*\`\`" | tr -d \` | awk '{printf " (\"%s\",``%s``);\n", $0,$0}'; 
echo "|]" 

這給:

let pairs = [| 
    ("Tail length plus tree length should equal vector length",``Tail length plus tree length should equal vector length``); 
    ("The tail may only be empty iff the vector is empty",``The tail may only be empty iff the vector is empty``); 
    ("The tail's length may not exceed tailMax",``The tail's length may not exceed tailMax``); 
    ("If vec.len <= tailMax, all items are in tail and root is empty",``If vec.len <= tailMax, all items are in tail and root is empty``); 
|] 
+0

這仍然是複製粘貼,但它是一種自動方式,以確保在添加新屬性或決定更改現有屬性名稱時更新屬性對。所以雖然Tomas Petricek的回答非常好,但我已經接受了你的回答,因爲在我的特殊情況下,它實際上幫助了我更多。 – rmunn

+0

那麼,你可以使用一個makefile來實現這個單獨的文件,並使其以正確的方式得到更新 –

5

運行FsCheck測試的最佳選擇是與一些單元測試一起使用FsCheck亞軍。測試運行器負責查找所有屬性,運行它們並在出現問題時打印出錯的錯誤日誌。

在.NET中最常見的兩種測試運行與FsCheck都工作都的xUnit和NUnit和FsCheck文檔介紹瞭如何使用它們:

在在這兩種情況下,您都使用[<Property>]屬性標記屬性,測試運行程序將使用它來查找它們並使用FsCheck爲您運行它們。所以,你需要這樣的東西:

[<Property>] 
let ``Tail length plus tree length should equal vector length`` vec = 
    treeLength vec.root + Array.length vec.tail = vec.len 

[<Property>] 
let ``The tail may only be empty iff the vector is empty`` vec = 
    (vec.len = 0) = (Array.length vec.tail = 0) 

[<Property>] 
let ``The tail's length may not exceed tailMax`` vec = 
    Array.length vec.tail < tailMax 

[<Property>] 
let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec = 
    (vec.len > tailMax) || (Array.length vec.root = 0) 
+0

儘管這是一個很好的建議,並且值得讚賞,但我還是爲我的特殊情況提供了其他答案,因爲測試看起來像「在數據結構上執行一堆操作,然後驗證所有屬性仍然是真的。」因此,我不想使用''[]'進行測試,我想寫'let vec'= vec |> push「foo」|> insert 3「bar」|> pop; checkAllProperties vec''。爲此,John Palmer建議的「hacky bash腳本」更有用。但是,謝謝你的好建議;我會在其他測試中使用'[]'。 – rmunn