citoz
citoz

Reputation: 55

Haskell: min distance between neighbor numbers on a list

I'm trying to define a functino that finds the minimum distance between to neighbor numbers on a list

something like this:

minNeighborsDistance [2,3,6,2,0,1,9,8] => 1

My code looks like this:

minNeighborsDistance [] = []
minNeighborsDistance (x:xs) = minimum[minNeighborsDistance xs ++ [subtract x (head xs)]]

Although this seems to run, once I enter a list I receive an Exception error.

I'm new to Haskell I would appreciate any help in this matter.

Upvotes: 1

Views: 1047

Answers (4)

bheklilr
bheklilr

Reputation: 54058

Your question is a bit unclear (a type signature would really help here), but if you're wanting to calculate the difference between adjacent elements of the list, then find the minimum of those numbers, I would say the most clear way is to use some extra pattern matching:

-- Is this type you want the function to have?
minNeighborsDistance :: [Int] -> Int
minNeighborsDistance list = minimum $ go list
    where
        go (x:y:rest) = (x - y) : go (y:rest)
        go anythingElse = []        -- Or just go _ = []

However, this won't quite give you the answer you want, because the actual minimum for your example list would be -4 when you go from 6 to 2. But this is an easy fix, just apply abs:

minNeighborsDistance :: [Int] -> Int
minNeighborsDistance list = minimum $ go list
    where
        go (x:y:rest) = abs (x - y) : go (y:rest)
        go anythingElse = []

I've used a helper function to calculate the differences from element to element, then the top-level definition calls minimum on that result to get the final answer.

There is an easier way, though, if you exploit a few functions in Prelude, namely zipWith, map, and drop:

minNeighborsDistance :: [Int] -> Int
minNeighborsDistance list
    = minimum        -- Calculates the minimum of all the distances
    $ (maxBound:)    -- Ensures we have at least 1 number to pass to
                     -- minimum by consing the maximum possible Int
    $ map abs        -- Ensure all differences are non-negative
    -- Compute the difference between each element.  I use "drop 1"
    -- instead of tail because it won't error on an empty list
    $ zipWith (-) list (drop 1 list)

So combined into one line without comments:

minNeighborsDistance list = minimum $ (maxBound:) $ map abs $ zipWith (-) list $ drop 1 list

Upvotes: 2

ThreeFx
ThreeFx

Reputation: 7350

Try this:

minDistance list = minimum (distance list)
  where
    distance list = map abs $ zipWith (-) list (tail list)

distance calculates the absolute value of the list being subtracted with itself shifted by 1 position:

  [2,3,6,2,0,1,9,8] -- the 8 is skipped but it does not make a difference
- [3,6,2,0,1,9,8]
= [1,3,4,2,1,8,1]

minDistance now just gets the smallest element of the resulting list.

Upvotes: 2

amnn
amnn

Reputation: 3716

Here's what I came up with:

minDistance l = minimum . map abs . zipWith (-) l $ tail l

Upvotes: 3

J. Abrahamson
J. Abrahamson

Reputation: 74344

If you pass a singleton list to minNeighborsDistance then

  1. It'll fail to match [] in the first line, then
  2. it'll successfully match (x:xs) assigning the single value to x and the empty like to xs, then
  3. it'll throw an error when you try to access the head of an empty list.

Further, since you call minNeighborsDistance recursively then you'll always eventually call it on a singleton list excepting when you pass it an empty list.

Upvotes: 6

Related Questions