2010-03-04 18 views
9

Mam funkcję postaciStosując zmienną wzór pasujący w Ocaml lub F #

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

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

mogę powiedzieć, że identifier nie jest związany sposób chciałbym go i działa jako nowej zmiennej w wyrażeniu dopasowania. Jak uzyskać identifier za to, co jest przekazywane do funkcji?

OK! Naprawiłem go za pomocą ochronnego wzoru, tj. | (i, value)::tail when i = indentifier -> value , ale uważam, że jest to brzydkie w porównaniu do tego, jak pierwotnie chciałem to zrobić (używam tylko tych języków, ponieważ są ładne ...). jakieś pomysły?

+0

Twoje oryginalne podejście przypomina mi zjednoczenie Prologu, jest raczej deklaratywne niż funkcjonalne. – ron

Odpowiedz

10

Możesz użyć aktywnych wzorców F #, aby utworzyć wzór, który zrobi dokładnie to, czego potrzebujesz. F # obsługuje sparametryzowane aktywne wzory, które przyjmują wartość, która pasujesz, ale także bierze dodatkowy parametr.

Tu jest całkiem głupi przykład, że nie powiedzie się, gdy value wynosi zero i inaczej się powiedzie i zwraca dodanie wartości i określony parametr:

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

Można określić parametr w wzór pasujący tak:

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

teraz możemy łatwo zdefiniować aktywną wzór, który będzie porównywał dopasowaną wartość z argumentem wejściowym aktywnego wzoru. Aktywny wzór zwraca unit option, co oznacza, że ​​nie wiąże żadnej nowej wartości (w przykładzie powyżej, powrócił pewną wartość, że przypisany do symbolu res):

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

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

Można to wykorzystać jako zagnieżdżony wzór, więc powinieneś być w stanie przepisać swój przykład przy użyciu aktywnego wzoru Equals.

+0

Interesujące (pisanie aktywnych wzorców dla danego typu musi wyglądać jak wzornik, ale zapisujesz je tylko raz i wiele razy ich używasz). Jak pojawiają się w podpisie modułu? –

+0

Aktywny wzór 'Equals' jest ogólny i działa dla dowolnego typu, który obsługuje porównanie (specjalne ograniczenie dla zmiennych typu dostępnych w F #). Aktywne wzory pojawiają się jako funkcje specjalnej nazwy w podpisie. Sygnatura wygląda tak: 'val (| Równa | _ |): 'a ->' a -> opcja jednostki, gdy" a: równość " –

3

To jest powszechna skarga, ale nie sądzę, że ogólnie istnieje dobre obejście tego problemu; wzór straży jest zwykle najlepszym kompromisem. W niektórych szczególnych przypadkach istnieją alternatywy, takie jak oznaczanie literałów z atrybutem [<Literal>] w języku F #, aby można je było dopasować.

5

Nie jest to bezpośrednio odpowiedź na pytanie: jak dopasować wzór do wartości zmiennej. Ale nie jest też całkowicie niepowiązany.

Jeśli chcesz zobaczyć, jak skuteczne dopasowanie wzorców może być w języku podobnym do ML podobnym do F # lub OCaml, spójrz na Moca.

Możesz również rzucić okiem na kod wygenerowany przez Mocę :) (nie, że coś jest nie tak z kompilatorem robiącym wiele rzeczy dla Ciebie w plecach, w niektórych przypadkach jest to pożądane, nawet, ale wielu programistów lubią czuć, że wiedzą, ile kosztują operacje, które piszą).

+0

+1: super! Wygląda na interesujący wybór do implementacji DSL. – Juliet

6

Jednym z najpiękniejszych języków funkcyjnych są funkcje wyższego rzędu. Korzystając z tych funkcji, wyciągamy rekurencję i skupiamy się na tym, co naprawdę chcemy zrobić. Który jest, aby uzyskać wartość pierwszego krotki pasującej swój identyfikator inaczej zwracają -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 

Ten paper Graham Hutton jest doskonałym wstępem do tego, co można zrobić z funkcja wyższego rzędu.

+1

* oczy eksplodują * Styl bez punktów jest sprytny, ale nieczytelny i nie do pomylenia. 'fun x -> x |> List.map fst |> List.filter (fun y -> y = identifier)' wyraża to samo bez utraty czytelności. – Juliet

+0

Punkt wzięty. Mam nadzieję, że ta poprawka będzie lepsza. :) –

+0

Prawda, tryFind byłby najlepszym użyciem tego języka, ponieważ tymczasowo używałam -1 jako stand-in dla None. Ale cieszę się, że nauczyłem się trochę więcej o dopasowywaniu wzorów i wiązaniach zmiennych. –

2

To, co próbujesz zrobić, jest nazywane wzorem równości i nie jest dostarczane przez Objective Caml. Wzorce obiektywu Camla są statyczne i czysto strukturalne. To znaczy, czy wartość pasuje do wzorca zależy wyłącznie od struktury wartości, i w sposób określony podczas kompilacji. Na przykład: (_, _)::tail to wzór pasujący do dowolnej niepustej listy, której głowa to para. (identifier, value)::tail dopasowuje dokładnie te same wartości; jedyną różnicą jest to, że ta ostatnia wiąże dwie kolejne nazwy: identifier i value.

Chociaż niektóre języki mają wzorce równości, istnieją nietrywialne względy praktyczne, które sprawiają, że są kłopotliwe. Która równość? Równość fizyczna (== w Ocaml), równość strukturalna (= w Ocaml), czy też zależność od typu zależna od typu? Ponadto, w Ocaml, istnieje wyraźne wskazanie składniowe, które nazwy są wiążące i które nazwy odnoszą się do wcześniej powiązanych wartości: dowolny mały identyfikator we wzorze jest spoiwem. Te dwa powody wyjaśniają, dlaczego Ocaml nie ma wypalonych wzorów równości. Idiomatyczny sposób wyrażenia wzoru równości w Ocaml jest w straży. W ten sposób natychmiast staje się jasne, że dopasowanie nie ma charakteru strukturalnego, że nie jest związane tym dopasowaniem do wzorca i które z nich jest równe. Co do brzydkiego, to jest w oku patrzącego - jak zwykły programista Ocaml, uważam wzorce równości za brzydkie (z powyższych powodów).

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

w F #, masz inną możliwość: active patterns, który pozwoli Ci wstępnie określić strażników, które dotyczą pojedyncze miejsce w strukturze.