Domanda

Ho cercato di spiegare la differenza tra le istruzioni switch e il pattern matching (F #) a un paio di persone, ma non sono stato davvero in grado di spiegarlo bene ... il più delle volte mi guardano e dicono " quindi perché non usi semplicemente if..then..else " ;.

Come lo spiegheresti a loro?

EDIT! Grazie a tutti per le ottime risposte, vorrei davvero poter contrassegnare più risposte giuste.

È stato utile?

Soluzione

Essendo stato in precedenza una di quelle "persone", non so che ci sia un modo sintetico per riassumere perché la corrispondenza dei modelli sia così gustosa bontà. È esperienziale.

Quando avevo appena dato un'occhiata al pattern-matching e pensavo fosse un'istruzione switch glorificata, penso che non avevo esperienza di programmazione con tipi di dati algebrici (tuple e sindacati discriminati) e non vedevo proprio quel modello la corrispondenza era sia un costrutto di controllo che un costrutto vincolante. Ora che sto programmando con F #, finalmente ho "capito". La freddezza del pattern-matching è dovuta a una confluenza di funzionalità che si trovano nei linguaggi di programmazione funzionale, quindi non è banale per l'apprezzamento dell'esterno.

Ho cercato di riassumere un aspetto del perché la corrispondenza dei modelli è utile nella seconda di una breve serie di blog in due parti sulla progettazione del linguaggio e delle API; controlla prima parte e seconda parte .

Altri suggerimenti

I pattern ti offrono un linguaggio di piccole dimensioni per descrivere la struttura dei valori che desideri abbinare. La struttura può essere arbitrariamente profonda e puoi associare le variabili a parti del valore strutturato.

Questo ti permette di scrivere cose in modo estremamente succinto. Puoi illustrarlo con un piccolo esempio, come una funzione derivata per un semplice tipo di espressioni matematiche:

type expr =
    | Int of int
    | Var of string
    | Add of expr * expr
    | Mul of expr * expr;;

let rec d(f, x) =
    match f with
    | Var y when x=y -> Int 1
    | Int _ | Var _ -> Int 0
    | Add(f, g) -> Add(d(f, x), d(g, x))
    | Mul(f, g) -> Add(Mul(f, d(g, x)), Mul(g, d(f, x)));;

Inoltre, poiché la corrispondenza dei modelli è un costrutto statico per tipi statici, il compilatore può (i) verificare di aver coperto tutti i casi (ii) rilevare rami ridondanti che non possono mai corrispondere a nessun valore (iii) fornire un'implementazione molto efficiente (con salti ecc.).

Estratto da questo articolo del blog :

La corrispondenza dei modelli presenta numerosi vantaggi rispetto alle istruzioni switch e all'invio del metodo:

  • Le corrispondenze di pattern possono agire su ints, galleggianti, stringhe e altri tipi come così come gli oggetti.
  • Le combinazioni di modelli possono agire su diversi valori diversi contemporaneamente: corrispondenza del modello parallelo. Metodo spedizione e cambio sono limitati a un singolo valore, ad es. & Quot; questo ".
  • I motivi possono essere nidificati, permettendo spedizione su alberi di arbitraggio profondità. L'invio e lo scambio del metodo sono limitati nel caso non nidificato.
  • I pattern Or consentono che siano i sottotatri condivisa. Il metodo di invio consente solo condivisione quando provengono i metodi classi che condividono una base classe. Altrimenti è necessario manualmente fattorizzare la comunanza in a funzione separata (assegnandole a nome), quindi inserire manualmente le chiamate da tutti i luoghi appropriati al tuo funzione non necessaria.
  • La corrispondenza dei modelli fornisce ridondanza verifica che rileva errori.
  • Pattern nidificato e / o parallelo le partite sono ottimizzate per te dal Compilatore F #. L'equivalente OO deve essere scritto a mano e costantemente riaperto a mano durante sviluppo, che è proibitivo noioso e soggetto a errori così il codice OO di qualità di produzione tende a essere estremamente lento in confronto.
  • I pattern attivi ti consentono di iniettare semantica della spedizione personalizzata.

In cima alla mia testa:

  1. Il compilatore può dire se non hai coperto tutte le possibilità nelle tue partite
  2. Puoi utilizzare una partita come compito
  3. Se hai un'unione discriminata, ogni partita può avere un 'tipo' diverso

Switch è le due ruote anteriori.

Il pattern matching è l'intera macchina.

Le tuple hanno ", " e le varianti hanno argomenti di Ctor .. questi sono costruttori, creano cose.

I pattern sono distruttori, li fanno a pezzi.

Sono due concetti.

Per dirlo più energicamente: la nozione di tupla o variante non può essere descritta semplicemente dal suo costruttore: il distruttore è richiesto o il valore che hai creato è inutile. Sono queste doppie descrizioni che definiscono un valore.

Generalmente pensiamo ai costruttori come dati e ai distruttori come flusso di controllo. I distruttori di variante sono rami alternativi (uno dei tanti), i distruttori di tupla sono fili paralleli (tutti i molti).

Il parallelismo è evidente in operazioni come

(f * g) . (h * k) = (f . h * g . k) 

se si pensa al controllo che scorre attraverso una funzione, le tuple forniscono un modo per suddividere un calcolo in thread di controllo paralleli.

In questo modo, le espressioni sono modi per comporre tuple e varianti per creare strutture dati complicate (si pensi a un AST).

E i pattern match sono modi per comporre i distruttori (di nuovo, pensa a un AST).

Le corrispondenze di pattern in OCaml, oltre ad essere più espressive come menzionato in diversi modi che sono stati descritti sopra, offrono anche importanti garanzie statiche. Il compilatore dimostrerà per te che l'analisi del caso incarnata dall'istruzione pattern-match è:

  • esaustivo (non mancano casi)
  • non ridondanti (nessun caso che non può mai essere colpito perché sono preceduti da un caso precedente)
  • suono (nessun modello impossibile dato il tipo di dati in questione)

Questo è davvero un grosso problema. È utile quando si scrive il programma per la prima volta e enormemente utile quando il programma si sta evolvendo. Se utilizzate correttamente, le istruzioni di corrispondenza semplificano la modifica affidabile dei tipi nel codice, poiché il sistema dei tipi indica le istruzioni di corrispondenza interrotte, che sono un indicatore decente di dove è necessario correggere il codice.

Le

istruzioni If-Else (o switch) riguardano la scelta di modi diversi di elaborare un valore (input) in base alle proprietà del valore attuale.

La corrispondenza dei pattern riguarda la definizione di come elaborare un valore data la sua struttura , (si noti anche che le corrispondenze dei pattern a caso singolo hanno senso).

Quindi la corrispondenza dei modelli è più una questione di decostruzione dei valori che di fare delle scelte, ciò li rende un meccanismo molto conveniente per la definizione di funzioni (ricorsive) su strutture induttive (tipi di unione ricorsivi), il che spiega perché sono così ampiamente utilizzate in linguaggi come Ocaml ecc. .

PS: potresti conoscere il pattern-match e If-Else " pattern " dal loro uso ad hoc in matematica;

" se x ha proprietà A, allora y z " (If-else)

"qualche termine in p1..pn dove .... è la scomposizione primaria di x .." ((caso singolo) corrispondenza del modello)

Forse potresti disegnare un'analogia con stringhe ed espressioni regolari? Descrivi cosa stai cercando e lasci che il compilatore capisca come da solo. Rende il tuo codice molto più semplice e chiaro.

A parte: trovo che la cosa più utile sulla corrispondenza dei modelli sia che incoraggia le buone abitudini. Mi occupo dei casi angolari prima , ed è facile verificare che ho coperto tutti i casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top