2010-03-04 3 views
9

Ho una funzione della formaUtilizzo di una variabile nel pattern matching in OCaml o F #

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

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

Posso dire che identifier non è vincolati come mi piacerebbe che e agisce come una nuova variabile all'interno dell'espressione della partita. Come si ottiene identifier per essere trasferito nella funzione?

Ok! L'ho risolto con una guardia di pattern, ovvero | (i, value)::tail when i = indentifier -> value ma trovo questo brutto rispetto al modo in cui inizialmente volevo farlo (sto solo usando queste lingue perché sono belle ...). qualche idea?

+0

Il tuo approccio originale mi ricorda l'unificazione di Prolog, è piuttosto dichiarativo che funzionale. – ron

risposta

10

È possibile utilizzare i modelli attivi F # per creare un modello che farà esattamente ciò di cui si ha bisogno. F # supporta pattern attivi parametrizzati che prendono il valore che stai cercando, ma prendono anche un parametro aggiuntivo.

Ecco un esempio abbastanza stupido che non riesce quando il value è zero ed al contrario riesce e restituisce l'aggiunta del valore e parametro specificato:

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

È possibile specificare il parametro nel pattern matching in questo modo:

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

Ora, possiamo facilmente definire un modello attivo che confronterà il valore con l'argomento di input del modello attivo. Il modello attivo ritorna unit option, il che significa che non vincola qualsiasi nuovo valore (nell'esempio di cui sopra, è tornato un po 'di valore che abbiamo assegnato a un simbolo res):

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

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

È possibile utilizzare questo come un nidificato pattern, quindi dovresti essere in grado di riscrivere il tuo esempio usando il pattern attivo Equals.

+0

Interessante (la scrittura dei modelli attivi per un tipo deve sentirsi come boilerplate, ma scrivere solo una volta e approfittare di loro molte volte). Come si presentano nella firma di un modulo? –

+0

Il modello attivo 'Equals' è generico e funziona per qualsiasi tipo che supporta il confronto (un vincolo speciale sulle variabili di tipo disponibili in F #). I modelli attivi mostrano come funzioni un nome speciale nella firma. La firma è simile al seguente: 'val (| Equals | _ |): 'a ->' a -> opzione di unità quando 'una: equality' –

3

Questa è una lamentela comune, ma non penso che ci sia una soluzione buona in generale; un modello di protezione è di solito il miglior compromesso. In alcuni casi specifici, tuttavia, esistono alternative, ad esempio il contrassegno dei valori letterali con l'attributo [<Literal>] in F # in modo che possano essere confrontati.

5

Questa non è direttamente una risposta alla domanda: come associare il modello a un valore di una variabile. Ma non è del tutto estraneo.

Se si desidera vedere quanto sia potente la corrispondenza del modello in un linguaggio simile a ML simile a F # o OCaml, dare un'occhiata a Moca.

Puoi anche dare un'occhiata al codice generato da Moca :) (non che ci sia qualcosa di sbagliato nel compilatore che fa un sacco di cose per te nella tua schiena. In alcuni casi, è auspicabile, anche, ma molti programmatori piace sentire di sapere quali saranno le operazioni che stanno scrivendo).

+0

+1: fantastico! Sembra una scelta interessante per l'implementazione di DSL. – Juliet

6

Una delle bellezze dei linguaggi funzionali è le funzioni di ordine superiore. Usando queste funzioni prendiamo la ricorsione e concentriamoci su ciò che vuoi veramente fare. Che è quello di ottenere il valore della prima tupla che corrisponde al proprio identificativo altrimenti restituisce -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 

Questo paper da Graham Hutton è una grande introduzione a quello che si può fare con funzioni di ordine superiore.

+1

occhi * * esplodono stile libero-Point è intelligente, ma illeggibile e impossibile da mantenere. 'fun x -> x |> List.map fst |> List.filter (fun y -> y = identificatore)' esprime la stessa cosa senza sacrificare la leggibilità. – Juliet

+0

Punto preso. Spero che questa revisione sia migliore. :) –

+0

Vero, tryFind sarebbe l'uso migliore della lingua dato che stavo usando temporaneamente -1 come sostituto di None. Ma sono contento di aver imparato un po 'di più sulla corrispondenza dei pattern e sul binding variabile. –

2

Quello che stai cercando di fare è chiamato un modello di uguaglianza, e non è fornito da Objective Caml. Obiettivo I modelli di Caml sono statici e puramente strutturali. Ovvero, se un valore corrisponde al modello dipende esclusivamente dalla struttura del valore e in un modo determinato al momento della compilazione. Ad esempio, (_, _)::tail è un modello che corrisponde a qualsiasi elenco non vuoto la cui testa è una coppia. (identifier, value)::tail corrisponde esattamente agli stessi valori; l'unica differenza è che quest'ultimo si lega altri due nomi identifier e value.

Sebbene alcune lingue abbiano schemi di uguaglianza, ci sono considerazioni pratiche non banali che le rendono problematiche. Quale uguaglianza? uguaglianza fisica (== in OCaml), l'uguaglianza strutturale (= in OCaml), o qualche tipo dipendente uguaglianza personalizzato? Inoltre, in Ocaml, esiste una chiara indicazione sintattica di quali nomi sono i raccoglitori e quali nomi fanno riferimento a valori precedentemente associati: qualsiasi identificatore minuscolo in un modello è un raccoglitore. Queste due ragioni spiegano perché Ocaml non ha schemi di uguaglianza. Il modo idiomatico di esprimere un modello di uguaglianza in Ocaml è in una guardia. In questo modo, è immediatamente chiaro che la corrispondenza non è strutturale, che identifier non è vincolato da questo abbinamento di modelli e che l'uguaglianza è in uso. Per quanto riguarda il brutto, questo è negli occhi di chi guarda - come abituale programmatore Ocaml, trovo brutti i modelli di uguaglianza (per le ragioni sopra).

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

in F #, hai un'altra possibilità: active patterns, che consentono di predefinire le guardie che riguardano un singolo sito in un modello.