Domanda

Ho appena notato che F # mi permette di usare le associazioni let con valori letterali e altri schemi come segue:

let fib 0 = 1
let exists item [] = false
let car (hd :: tl) = hd
let cdr (hd :: tl) = tl

F # interpreta correttamente queste funzioni come una specie di corrispondenza del motivo, perché mi dà i seguenti avvertimenti:

  

Avviso 1 Corrispondenze di schemi incomplete   su questa espressione. Ad esempio, il   il valore '1' non sarà   abbinato

     

Avviso 2 Corrispondenze di schemi incomplete   su questa espressione. Ad esempio, il   il valore '[_]' non sarà   abbinato

     

ecc.

Queste funzioni funzionano come previsto, ma voglio definire una funzione in questo stile con corrispondenze di pattern complete, tuttavia non riesco a trovare nulla su questa sintassi di corrispondenza di pattern alternativa nel manuale di F #.

So di poter usare let qualunque = funzione ... e lasciare qualunque x = abbinare x con ... per ottenere i risultati che desidero, ma io ' ho appena scoperto l'ennesima sintassi per la corrispondenza dei motivi e mi tormenterà per sempre se non capisco come usarlo.

Come posso scrivere le funzioni utilizzando la sintassi di corrispondenza dei motivi alternativa mostrata sopra?

È stato utile?

Soluzione

JaredPar ha ragione, F # non ha la forma sintattica che Haskell ha qui.

Il modulo F # è per lo più utile per rompere i sindacati discriminati a caso singolo o per definire funzioni con corrispondenze incomplete (come l'esempio della tua "auto" che fallisce nell'elenco vuoto). È semplicemente una conseguenza del fatto che praticamente tutti i legami del nome nella lingua sono fatti tramite schemi; questa forma sintattica (definire una funzione usando schemi su argomenti) non è spesso troppo utile in pratica, per il motivo esatto che hai descritto.

Penso che Haskell abbia fatto molte cose meglio di ML quando si tratta di forme sintattiche, ma le radici di F # sono in ML. Il vantaggio è che un buon sottoinsieme di cross-compilazioni di F # con OCaml (che ha aiutato a avviare il linguaggio F # e la comunità di utenti); lo svantaggio è che F # è "bloccato" con alcuni bit di sintassi brutta / limitata.

Altri suggerimenti

AFAIK, in F # non è possibile dichiarare più associazioni let con lo stesso nome e firme di corrispondenza dei pattern differenti. Credo che il costrutto più vicino a quello che stai cercando sia un'espressione delle regole di funzione.

Prendi questo esempio per auto

let car = function
    | hd::tl -> hd
    | [] -> failwith "empty list"

Apparentemente, il pattern matching di F # è molto più potente di quello che usiamo nello sviluppo comune.

Innanzitutto, puoi associare più valori contemporaneamente. In genere, lo farai con List.partition :

let data = [7;0;0;0;1;0;1;1;1;1;0;0;1;1;0]
let ones, others = data |> List.partition ((=) 1) // bind two values

Come nota a margine, puoi associare diversi identificatori allo stesso valore:

let (a & b) = 42 // a = 42; b = 42

Cominciamo con un semplice let per semplicità.

let hd::tl = data
  

avviso FS0025 : il pattern incompleto corrisponde a questa espressione. Ad esempio, il valore '[]' può indicare un caso non coperto dai motivi.

Per mitigare questo, dobbiamo aggiungere un altro caso per l'elenco vuoto:

let (hd::tl) | [] = data
  

errore FS0018 : i due lati di questo modello 'o' associano diversi set di variabili

Ed è vero; in caso di elenco vuoto, hd e tl rimangono non associati. È facile associare tl con lo stesso elenco vuoto:

let (hd::tl) | ([] as tl) = data

Tuttavia, l'errore errore FS0018 non scompare. In effetti, dobbiamo anche fornire alcuni "predefiniti" valore per hd .
Il seguente trucco sporco lo farà.

let (hd::tl, _) | ([] as tl , hd) = data, 42

La riga sopra legherà hd alla testa del data , nel caso in cui l'elenco non sia vuoto, o con il supplemento valore fornito nel secondo valore di tuple .

Nota Non ho trovato il modo di incorporare 42 nel costrutto let .

Infine, lo stesso vale per la funzione car :

let car ((hd::tl, _) | ([] as tl, hd)) = hd
let foo = car(data, 42) // foo = 7
let bar = car([], 42)   // bar = 42
  

Come scrivo le funzioni usando la sintassi alternativa di corrispondenza dei modelli mostrata sopra?

Come hai fatto, ma solo quando uno schema è esaustivo. Esempi ovvi in ??cui ciò è utile includono tuple e registrazioni, nonché unioni a caso singolo e schemi attivi.

Usi questa funzione della lingua ogni volta che lo fai:

let f(a, b) = ...

Quindi questo generalizza a:

let f(a, (b, c)) = ...

Puoi anche usarlo per scegliere tra un valore predefinito o Some value :

let def(x, None | _, Some x) = x

A proposito, lo stile che stai suggerendo è stato usato in SML prima di Haskell e SML è un ML quindi questo ovviamente non ha nulla a che fare con Haskell vs ML. In realtà preferisco lo stile OCaml / F # perché è meno ripetitivo:

fun descriptiveFunctionName [] = true
fun descriptiveFunctionName (_::_) = false

vs

let descriptiveFunctionName = function
  | [] -> true
  | _::_ -> false

Questo è meglio per il codice non accademico in cui c'è un valore reale è usare identificatori autocompensanti.

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