Distance between adjacent points in F#

Let’s say you are given a list of data points:

[7;5;12;8;5]

And you are asked to find the distance between every adjacent pair, that is:

[(7-5);(5-12);(12-8);(8-5)] = [2;-7;4;3]

It turns out that there is an elegant solution to this problem:

let rec pairs = function
  | h1 :: (h2 :: _ as tail) -> (h1, h2) :: pairs tail
  | _ –> []


let distances dataPoints =
  dataPoints |> pairs |> List.map (fun (a, b) -> a - b)

The magic happens in the pairs function, this function takes [7;5;12;8;5] and turns it into [(7,5);(5,12);(12,8);(8,5)], that is, it creates a tuple with every member of the list and its right neighbor. The trick is that tail gets bounded  to ( h2 :: _ ), so that the recursive call processes the list starting with h2. This is called a named subpattern, something I discovered in section 1.4.2.2 of F# for Scientists. You learn at least a nice thing every day!

5 Comments

  • You can have this even shorter:

    let distances pts = pts |> Seq.pairwise |> Seq.map (fun (a,b) -> a-b)

    this yields a "seq -> seq" that works with your list in the expected way

  • In Scala, I'd make a list of adjacent pairs like this:

    list.init.zip(list.tail)

    I assume this can be translated to F#.

  • Correction: the init is unnecessary, since zip ignores any extra elements.

    list.zip(list.tail)

  • @David

    Unfortunately the zip function in F# requires that both lists have the same size, so we need a little helper:

    let allButLast list = list |> List.rev |> List.tl |> List.rev

    Not the most efficient implementation :-) but you get my point. And then the translation of your Scala code would be:

    List.zip (allButLast list) list.Tail

    But yes, in this case your code is shorter (and probably more elegant) than mine.

  • Careful, your recursive pairs function isn't tail recurisve :)

Comments have been disabled for this content.