2017-04-17 1 views
4

A common surprise pour les programmeurs début F # est le fait que ce qui suit est une correspondance incomplète:correspondance incomplète modèle lorsque deux modèles partagent une `when` clause

let x, y = 5, 10 
match something with 
| _ when x < y -> "Less than" 
| _ when x = y -> "Equal" 
| _ when x > y -> "Greater than" 

Mais je rencontrais une situation qui m'a surpris. Voici un petit morceau de code de l'échantillon pour le démontrer:

type Tree = 
| Leaf of int 
| Branch of Tree list 

let sapling = Branch [Leaf 1] // Small tree with one leaf 
let twoLeafTree = Branch [Leaf 1; Leaf 2] 

let describe saplingsGetSpecialTreatment tree = 
    match tree with 
    | Leaf n 
    | Branch [Leaf n] when saplingsGetSpecialTreatment -> 
     sprintf "Either a leaf or a sapling containing %d" n 
    | Branch subTree -> 
     sprintf "Normal tree with sub-tree %A" subTree 

describe true sapling // Result: "Either a leaf or a sapling containing 1" 
describe false sapling // Result: "Normal tree with sub-tree [Leaf 1]" 
describe true twoLeafTree // Result: "Normal tree with sub-tree [Leaf 1; Leaf 2]" 
describe false twoLeafTree // Result: "Normal tree with sub-tree [Leaf 1; Leaf 2]" 

Cette version de la fonction describe produit le « modèle incomplet correspond à cette expression » avertissement, même si le match de modèle est, en fait, complet. Il n'y a pas d'arbres possibles qui ne seront pas abondés par ce match de modèle, comme on peut le voir par enlever la branche spécifique du match qui avait une expression when en elle:

let describe tree = 
    match tree with 
    | Leaf n -> sprintf "Leaf containing %d" n 
    | Branch subTree -> 
     sprintf "Normal tree with sub-tree %A" subTree 

Cette version de describe renvoie la Chaîne "Arbre normal" pour les arbres sapling et twoLeafTree.

Dans le cas où l'expression match ne contient que when expressions (comme le premier exemple où x et y sont comparés), il est raisonnable que le compilateur F # peut ne pas être en mesure de dire si le match sera terminé. Après tout, x et ypourrait être types avec un « bizarre » la mise en œuvre de comparaison et de l'égalité où aucune de ces trois branches sont vraies. *

Mais dans des cas comme ma fonction describe, pourquoi ne pas le F # compilateur regardez le motif, dites "Si toutes les expressions when évalués à false, il y aurait toujours une correspondance complète" et ignorer l'avertissement "incomplète motif correspond"? Y a-t-il une raison spécifique pour que cet avertissement apparaisse ici, ou est-ce juste un cas où le compilateur F # est juste un peu simpliste ici et donne un avertissement faussement positif parce que son code n'était pas assez sophistiqué?

* En fait, il est possible de définir x et y à des valeurs telles que x < y, x = y et x > y sont tous faux, sans jamais marcher en dehors des limites « normales » du système standard de type .Net. En tant que question spéciale/puzzle, quelles sont ces valeurs de x et y? Aucun type personnalisé nécessaire pour répondre à ce casse-tête; tout ce dont vous avez besoin est de types fournis en standard .Net.

Répondre

8

In F syntaxe # match, les gardes when appliquent à tous cas énumérés juste avant, non seulement pour le dernier. Dans votre scénario spécifique, le dispositif de protection when saplingsGetSpecialTreatment s'applique aux cas Leaf n et Branch [Leaf n]. Donc, ce match va échouer dans le cas où tree = Leaf 42 && saplingsGetSpecialTreatment = false

Ce serait complet:

let describe saplingsGetSpecialTreatment tree = 
    match tree with 
    | Leaf n -> 
     sprintf "Either a leaf or a sapling containing %d" n 
    | Branch [Leaf n] when saplingsGetSpecialTreatment -> 
     sprintf "Either a leaf or a sapling containing %d" n 
    | Branch subTree -> 
     sprintf "Normal tree with sub-tree %A" subTree 
+3

Pour clarifier les choses, par "énuméré juste avant", vous voulez dire sans intervention '- > ' – bergey

+1

Oui, c'est ce que je veux dire. –

+1

Mon code original avait en fait le cas 'Branch' avec un traitement spécial * en premier * et le cas' Leaf n' en second. Le cas 'Branch' avait une condition' when', mais omettait un '->' car je voulais aussi faire correspondre le cas 'Leaf'. Et ce que j'ai eu, c'est l'erreur "Symbole inattendu" '' '" dans la correspondance de pattern, attendu '' -> '' ou un autre jeton. " À l'époque, je ne comprenais pas pourquoi cela avait échoué, mais le cas de 'Leaf' fonctionnait; Maintenant que je sais que les clauses 'when' s'appliquent à * tous * les patterns de ce" groupe "particulier (tous les patterns qui partagent un' -> '), je comprends l'erreur de syntaxe. Merci! – rmunn

1

Juste clarifier le poste de Fiodor avec un exemple supplémentaire.Pensez-y comme quand y = 3 section, une section autre, puis, pour tout le reste

let f y x = 
    match x with 
    | 0 
    | 1 
    | 2 when y = 3 -> "a" 
    | 0 
    | 1 
    | 2   -> "b" 
    | _   -> "c" 

[0 .. 3] |> List.map (f 3) 
[0 .. 3] |> List.map (f 2) 

FSI

val f : y:int -> x:int -> string 

> val it : string list = ["a"; "a"; "a"; "c"] 

> val it : string list = ["b"; "b"; "b"; "c"] 

Alors, est-ce un défaut raisonnable? Je le pense.

Voici une version plus explicite:

let f2 y x = 
    match x,y with 
    | 0,3 
    | 0,3 
    | 0,3 -> "a" 
    | 0,_ 
    | 1,_ 
    | 2,_ -> "b" 
    | _ -> "c" 

[0 .. 3] |> List.map (f2 3) 
[0 .. 3] |> List.map (f2 2) 

... et une version plus compacte:

let f3 y x = x |> function | 0 | 1 | 2 when y = 3 -> "a" 
          | 0 | 1 | 2 -> "b" 
          | _ -> "c"