2010-03-04 61 views
9

我有以下形式的功能使用模式OCaml中或F#匹配一個變量

'a -> ('a * int) list -> int 

let rec getValue identifier bindings = 
    match bindings with 
    | (identifier, value)::tail -> value 
    | (_, _)::tail -> getValue identifier tail 
    | [] -> -1 

我可以告訴大家,identifier不被束縛我會把它喜歡的方式,並作爲一個新的變量在匹配表達式內。如何獲得identifier是傳遞給函數的內容?

好的!我用一個模式衛士固定它,即| (i, value)::tail when i = indentifier -> value 但我覺得這比我原本想做它的方式更醜陋(我只使用這些語言,因爲它們很漂亮......)。有什麼想法嗎?

+0

您的原始方法讓我想起了Prolog的統一性,它比功能性更具說明性。 – ron 2010-03-08 22:59:30

回答

10

您可以使用F#活動模式創建一個模式,它將完全按照您的需要進行操作。 F#支持採用您匹配的值的參數化活動模式,但也需要一個附加參數。

這裏是當value是零,否則成功並返回添加值和指定的參數失敗一個非常愚蠢的例子:

let (|Test|_|) arg value = 
    if value = 0 then None else Some(value + arg) 

您可以指定模式匹配這樣的參數:

match 1 with 
| Test 100 res -> res // 'res' will be 101 

現在,我們可以很容易地定義一個活動模式,它將比較匹配值和活動模式的輸入參數。有源圖案返回unit option,這意味着它不結合任何新的值(在上面的例子中,它返回,我們分配給符號res一些值):

let (|Equals|_|) arg x = 
    if (arg = x) then Some() else None 

let foo x y = 
    match x with 
    | Equals y -> "equal" 
    | _ -> "not equal" 

可以使用此作爲嵌套模式,因此您應該能夠使用Equals活動模式重寫您的示例。

+0

有趣的是(一種類型的活動模式的寫作必須感覺像樣板,但你只寫了一次,並多次利用它們)。它們如何顯示在模塊簽名中? – 2010-03-04 22:03:46

+0

「Equals」活動模式是通用的,適用於支持比較的任何類型(對F#中可用類型變量的特殊限制)。活動模式顯示爲簽名中特殊名稱的功能。簽名看起來像這樣:當'a:等於'時,'val(| Equals | _ |):'a - >'a - >單位選項 – 2010-03-04 22:10:18

3

這是一個常見的投訴,但我不認爲總體上有一個很好的解決方法;模式守衛通常是最好的折衷方案。但是,在某些特定情況下,還有其他替代方法,例如在F#中標記具有[<Literal>]屬性的文字,以便它們可以匹配。

5

這不是直接回答這個問題:如何模式匹配變量的值。但它也不完全無關。

如果您想了解類似於F#或OCaml的類似ML的語言中模式匹配的強大功能,請參閱Moca。你也可以看看Moca生成的代碼:)(並不是說編譯器在你的背後爲你做了很多事情有什麼問題,在某些情況下,甚至是可取的,但是很多程序員喜歡感覺他們知道他們寫的操作會花費多少)。

+0

+1:cool!看起來像是實施DSL的有趣選擇。 – Juliet 2010-03-04 22:24:23

6

功能語言的美麗之一是高階函數。使用這些函數,我們將遞歸出來,並專注於你真正想做的事情。這是讓你的標識符匹配的第一個元組的值,否則返回-1:

let getValue identifier list = 
match List.tryFind (fun (x,y) -> x = identifier) list with 
    | None  -> -1 
    | Some(x,y) -> y 

//val getValue : 'a -> (('a * int) list -> int) when 'a : equality 

paper格雷厄姆·赫頓是一個偉大的介紹你可以用高階函數做什麼。

+1

*眼睛爆炸*無點式風格很聰明,但無法閱讀且無法維護。 'fun x - > x |> List.map fst |> List.filter(fun y - > y = identifier)'表示相同的事物而不犧牲可讀性。 – Juliet 2010-03-04 22:22:42

+0

採取的一點。我希望這次修改更好。 :) – 2010-03-04 22:43:48

+0

確實,tryFind將是該語言的最佳用法,因爲我只暫時使用-1作爲None的替身。但我很高興我學到了更多關於模式匹配和變量綁定的知識。 – 2010-03-05 08:01:28

2

你試圖做的就是所謂的平等模式,它不是由Objective Caml提供的。目標Caml的模式是靜態的,純粹是結構性的。也就是說,值是否與模式匹配取決於值的結構,以及在編譯時確定的方式。例如,(_, _)::tail是一個匹配頭部爲一對的非空列表的模式。 (identifier, value)::tail完全匹配相同的值;唯一的區別是後者綁定了更多名稱identifiervalue

雖然有些語言具有平等模式,但有一些不平凡的實際考慮因素會使它們變得麻煩。哪種平等?物理平等(Occam中的==),結構相等(Ocaml中的=)或某種類型相關的自定義相等?此外,在Ocaml中,有一個明確的語法指示,指明哪些名稱是綁定器,哪些名稱引用了先前綁定的值:模式中的任何小寫標識符都是綁定器。這兩個原因可以解釋爲什麼Ocaml沒有出現平等模式。在Ocaml中表達平等模式的慣用方式是守衛。這樣,很明顯,匹配不是結構性的,identifier不受此模式匹配的約束,以及哪個相等性正在使用。至於醜陋,那是在旁觀者的眼中 - 作爲一個習慣性的Ocaml程序員,我覺得平等模式很醜陋(出於上述原因)。

match bindings with 
| (id, value)::tail when id = identifier -> value 
| (_, _)::tail -> getValue identifier tail 
| [] -> -1 

在F#中,你有另一種可能性:active patterns,讓您預先定義的模式涉及到一個站點衛士。