1

This is a somewhat beginner question. I have been trying to validate the following type of FamilyTree. I can't find a simple way to do this. All help would be appreciated.

type BirthYear = int;;
type Tree = Person of BirthYear * Children
and Children = Tree list;;

I want to validate a given family tree such that every Person is older than their Children and furthermore check if the list of Children is sorted in order of their age (eldest first). Preferably done with a function that return a boolean. Something along the lines of this:

let rec validate (Person(x,child)) = 
    let vali = child |> List.forall (fun (y,_) -> y < x)
Simon Emil
  • 85
  • 4
  • I've tried using List.forall and List.collect, but only for a simple function. I can't figure out the recursive part, so that I can go through the entire tree. – Simon Emil Nov 26 '14 at 16:40
  • Please share a snippet with what you have - I'm sure everyone will be more happy to help if you have something to start with. At least pattern matching on the tree, or something along those lines! – Tomas Petricek Nov 26 '14 at 16:41
  • above is the furthest I've come – Simon Emil Nov 26 '14 at 17:07

3 Answers3

4

I'd do something like this:

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

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

where minParentAge is set to a reasonable minimum age to have children at.

I'd expect checkAges to be the more difficult part here: the function checks whether the first child it sees is younger than the limit it is given, then recursively checks the next child, with the current child's age as the new limit.

Note some techniques:

  • The function that checks child ages takes the minimum birthday as input; this is used to validate that the parent is old enough for the first child to be reasonable.

  • List.forall checks a predicate for all items in a list, and early-outs if a predicate is not fulfilled

  • function is a shorthand to create a function that does pattern matching on its parameter. Therefore, checkAges actually has two arguments.

Vandroiy
  • 6,163
  • 1
  • 19
  • 28
3

Here's a very simple solution using a single recursive function. It's not relying on built-in functions like List.forall but I think it's very declarative and (hopefully) easy to follow.

  • Rule 1: Every Person is older than their Children
  • Rule 2: List of Children is sorted in order of their age (eldest first)

Code:

let rec isValid = function
    | Person ( _     , []) -> true // Person alone without childs -> always valid
    | Person (minYear, Person (year, childs) :: brothers) ->
        year > minYear &&          // Validate Rules (either 1 or 2)
        isValid (Person (year, childs)) && // Enforce Rule 1
        isValid (Person (year, brothers))  // Enforce Rule 2

I personally don't feel List.forall fits well here, it helps to solve a part of the problem but not the whole, so you need to combine it with more stuff (see the other answers) and in the end you can't avoid a recursive function.

List functions are good for lists but for trees I feel recursion more natural unless your tree provides already a way to traverse it.

Gus
  • 25,839
  • 2
  • 51
  • 76
  • This essentially copies the tree. – Daniel Nov 27 '14 at 04:45
  • Yes, but it re-create the nodes in order to match the arguments of the recursive functions, you can wrap it easily in a one liner non-recursive function in order to discard the DU and work directly with the lists. – Gus Nov 27 '14 at 06:24
2

Here's a way to do it. Perhaps spending some time analyzing how this works will be helpful to you.

let rec check (Person(age, children)) =
    match children with
    | [] -> true
    | Person(eldest, _)::_ -> 
        Seq.pairwise children |> Seq.forall ((<||) (>)) 
            && age > eldest 
            && List.forall check children
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • You're not making the understanding all that easy by using things like this `((<||)(>))` ;-) – Tomas Petricek Nov 26 '14 at 18:04
  • The inputs are actually not ages, but birthdates. (Years, as clarified in the question now.) So the inequality signs should be the other way around. Also, for children, it should permit equality, since it is possible to have two children in the same year. *BTW, I managed to combine our answers into one function with only three lines of body. Looks a little too crazy to post though. ;D* – Vandroiy Nov 26 '14 at 18:23