11
votes

Éviter la duplication de code dans F #

J'ai deux extraits de code qui essaie de convertir une liste de flotteurs en une liste Vector3 ou Vector2. L'idée est de prendre 2/3 éléments à la fois de la liste et de les combiner comme un vecteur. Le résultat final est une séquence de vecteurs.

    let rec vec3Seq floatList =
        seq {
            match floatList with
            | x::y::z::tail -> yield Vector3(x,y,z)
                               yield! vec3Seq tail
            | [] -> ()
            | _ -> failwith "float array not multiple of 3?"
            }

    let rec vec2Seq floatList =
        seq {
            match floatList with
            | x::y::tail -> yield Vector2(x,y)
                            yield! vec2Seq tail
            | [] -> ()
            | _ -> failwith "float array not multiple of 2?"
            }


2 commentaires

semble assez propre pour moi, je ne pense pas que vous rencontrez un problème de maintenabilité


Vous pouvez écrire un code générique qui pourrait attraper n articles, puis utiliser une correspondance pour choisir vector3 ou vector2 (selon le cas), mais pourquoi? Les frais généraux seraient plus compliqués que ce que vous avez ici. Maintenant, si vous allez jusqu'à 12 ans, c'est une autre histoire ....


4 Réponses :


2
votes

Comme Rex commenté, si vous le souhaitez que pour deux cas, vous n'aurez probablement aucun problème si vous laissez le code tel qu'il est. Toutefois, si vous souhaitez extraire un modèle commun, vous pouvez écrire une fonction qui scindre une liste dans la sous-liste d'une longueur spécifiée (2 ou 3 ou tout autre numéro). Une fois que vous faites cela, vous n'utiliserez que mapper pour activer chaque liste de la longueur spécifiée dans vecteur .

La fonction de la liste de fractionnement n'est pas disponible dans le F # Bibliothèque (autant que je puisse dire), vous devrez donc la mettre en œuvre vous-même. Il peut être fait à peu près à celui-ci: xxx

MAINTENANT, vous pouvez utiliser cette fonction pour implémenter vos deux conversions telles que ceci: xxx

Cela donnera un avertissement, car nous utilisons un modèle incomplet qui s'attend à ce que les listes retournées soient de longueur 2 ou 3 respectivement, mais c'est une attente correcte, le code fonctionnera donc bien. J'utilise également une brève version de Séquence Expression le -> fait la même chose que rendez , mais il ne peut être utilisé que dans des cas simples comme celui-ci.


0 commentaires

13
votes

Voici une approche. Je ne sais pas à quel point cela plus simple est vraiment, mais cela abstrait une partie de la logique répétée.

let rec mkSeq (|P|_|) x =
  seq {
    match x with
    | P(p,tail) -> 
        yield p
        yield! mkSeq (|P|_|) tail
    | [] -> ()
    | _ -> failwith "List length mismatch" }

let vec3Seq =
  mkSeq (function
  | x::y::z::tail -> Some(Vector3(x,y,z), tail)
  | _ -> None)


3 commentaires

Plus je regarde ça, plus je l'aime.


Bel usage d'un motif actif partiel.


Ceci est incroyablement élégant. Je n'ai jamais imaginé passer un modèle actif dans une fonction. Impressionnant. Merci pour ça.



0
votes

Honnêtement, ce que vous avez à peu près bien que possible, bien que vous puissiez pouvoir faire un peu plus compact en utilisant ceci: xxx

maintenant le processus de rupture de votre Les listes sont déplacées dans ses propres fonctions génériques "Prendre" et "Split", c'est beaucoup plus facile à mapper sur le type souhaité.


0 commentaires

2
votes

Ceci est simular à la solution de KVB mais n'utilise pas de modèle actif partiel.

let rec listToSeq convert (list:list<_>) =
    seq {
        if not(List.isEmpty list) then
            let list, vec = convert list
            yield vec
            yield! listToSeq convert list
        }

let vec2Seq = listToSeq (function
    | x::y::tail -> tail, Vector2(x,y)
    | _ -> failwith "float array not multiple of 2?")

let vec3Seq = listToSeq (function
    | x::y::z::tail -> tail, Vector3(x,y,z)
    | _ -> failwith "float array not multiple of 3?")


0 commentaires