4

This is a question that extends F# Recursive Tree Validation, which I had nicely answered yesterday.

This question concerns inserting a child in an existing tree. This is the updated type I'd like to use:

type Name           = string
type BirthYear      = int
type FamilyTree     = Person of Name * BirthYear * Children
and Children        = FamilyTree list

My last question concerned checking the validity of the tree, this was the solution I decided to go with:

let rec checkAges minBirth = function
    | Person(_,b,_) :: t -> b >= minBirth && checkAges b t
    | [] -> true

let rec validate (Person(_,b,c)) =
    List.forall isWF c && checkAges (b + 16) c

Now I would like to be able to insert a Person Simon as a child of specific Person Hans in the following form

insertChildOf "Hans" simon:Person casperFamily:FamilyTree;;

So, input should be parent name, child and the family tree. Ideally it should then return a modified family tree, that is FamilyTree option

What I am struggling with is to incorporating the validate function to make sure it is legal, and a way to insert it properly in the list of children, if the insertion Person is already a parent - maybe as a seperate function.

All help is welcome and very appreciated - thanks! :)

Community
  • 1
  • 1
Simon Emil
  • 85
  • 4
  • I guess if the name match and the age is invalid it should return None, but what if the name doesn't match any of those in the family tree? Should it return an unmodified tree? Have you think about those 2 different cases? And what if more than one name match? – Gus Nov 27 '14 at 17:32
  • First of all; thanks a lot for your time! The return value should be None in the case that the insertion is not possible. That is, if the age doesn't match or you can't find the name. For the time being lets just say that there'll be no duplicate of names :p – Simon Emil Nov 27 '14 at 17:45

1 Answers1

3

After your comment here's a code that will behave as expected:

let insert pntName (Person(_, newPrsnYear, _) as newPrsn) (Person (n,y,ch)) =
    let rec ins n y = function
        | [] -> if y < newPrsnYear && n = pntName then Some [newPrsn] else None
        | (Person (name, year, childs) as person) :: bros ->
            let tryNxtBros() = Option.map (fun x -> person::x) (ins n y bros)
            if y < newPrsnYear && n = pntName then // father OK
                if newPrsnYear < year then // brother OK -> insert here
                    Some (newPrsn::person::bros)
                else tryNxtBros()
            else // keep looking, first into eldest child ...
                match ins name year childs with
                | Some i -> Some (Person (name, year, i) :: bros)
                | _      -> tryNxtBros() // ... then into other childs
    Option.map (fun x -> Person (n, y, x)) (ins n y ch)

As in my previous answer I keep avoiding using List functions since I don't think they are a good fit in a tree structure unless the tree provides a traverse.

I might be a bit purist in the sense I use either List functions (with lambdas and combinators) or pure recursion, but in general I don't like mixing them.

Gus
  • 25,839
  • 2
  • 51
  • 76