2010-10-25 6 views
3

Je suis nouveau à F # et je me demande comment je vais aplatir une liste.F # - Aplatir liste/plage

Essentiellement dans la base de données, je stocke un enregistrement avec une plage min_age et max_age (c'est un exemple fictif pour des raisons de concision - je ne suis pas agist!). Mes champs ressembler à quelque chose comme ce qui suit:

id

, coût, économies, MIN_AGE, max_age

J'ai essentiellement une classe F # qui agit comme un à-un avec ce tableau - à savoir toutes les propriétés sont mappées exactement aux champs de la base de données.

Ce que je voudrais faire, c'est aplatir cette gamme. Ainsi, au lieu d'une liste contenant des articles comme celui-ci:

saving_id = 1, cost = 100, savings = 20, min_age = 20, max_age = 26 
saving_id = 2, cost = 110, savings = 10, min_age = 27, max_age = 31 

Je voudrais une liste contenant des articles comme celui-ci:

saving_id = 1, cost = 100, savings = 20, age = 20 
saving_id = 1, cost = 100, savings = 20, age = 21 
etc. 
saving_id = 2, cost = 110, savings = 10, age = 27 
saving_id = 2, cost = 110, savings = 10, age = 28 
etc. 

Y at-il mécanisme intégré pour aplatir une liste de cette manière et/ou quelqu'un sait-il comment y parvenir? Merci à l'avance,

JP

Répondre

8

Vous pouvez utiliser Seq.collect. Il concatène des séquences ensemble, donc dans votre cas, vous pouvez mapper une fonction sur votre entrée qui sépare un seul enregistrement de tranche d'âge en une séquence d'enregistrements d'âge et utiliser Seq.collect pour les assembler.

Par exemple:

type myRecord = 
{ saving_id: int; 
    cost: int; 
    savings: int; 
    min_age: int; 
    max_age: int } 

type resultRecord = 
    { saving_id: int; 
     cost: int; 
     savings: int; 
     age: int } 

let records = 
    [ { saving_id = 1; cost = 100; savings = 20; min_age = 20; max_age = 26 } 
     { saving_id = 2; cost = 110; savings = 10; min_age = 27; max_age = 31 } ] 

let splitRecord (r:myRecord) = 
    seq { for ageCounter in r.min_age .. r.max_age -> 
      { saving_id = r.saving_id; 
       cost = r.cost; 
       savings = r.savings; 
       age = ageCounter } 
    } 

let ageRanges = records |> Seq.collect splitRecord 

Edit: vous pouvez également utiliser un générateur de séquence avec un rendement!

let thisAlsoWorks = 
    seq { for r in records do yield! splitRecord r } 
+0

Bonne réponse. Je suppose que l'OP demande un «mécanisme intégré» qui ne fonctionnerait vraiment que s'il n'y avait pas de types d'enregistrements personnalisés impliqués, mais comme c'est cette solution qui semble aussi élégante que possible. –

+0

Une expression de séquence peut être une ligne générique élégante: 'aplatir seqOfSeq = seq {pour seq dans seqOfSeq ne cède pas! seq} '. – MasterMastic

1

D'accord avec la réponse de cfern, mais je me demandais si cela pourrait profiter de voir une autre fonction « intégrée » utilisé. Voici une autre version de la fonction splitRecord qui affiche l'appel de la bibliothèque pour le déroulement d'une séquence. Aucun gain ici d'autre que d'avoir un exemple pour Seq.unfold.

let splitRecord (r:myRecord) = 
    Seq.unfold (fun curr_age -> 
        if curr_age <= r.max_age then 
         Some({ saving_id = r.saving_id; 
           cost = r.cost; 
           savings = r.savings; 
           age = curr_age } , 
           curr_age + 1) 
        else None) 
       r.min_age 
+0

Solution intéressante :) Bien sûr, je trouve dans presque tous les cas, "tout ce qui donne de la valeur" est presque toujours plus lisible que Seq.unfold :) – Juliet

+0

@Juliet Je trouve qu'essayer de voir un cas pour les fonctions de la bibliothèque partout aide les rappeler devraient-ils devenir utiles. :) –