Reputation: 55
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
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
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
Reputation: 3716
Here's what I came up with:
minDistance l = minimum . map abs . zipWith (-) l $ tail l
Upvotes: 3
Reputation: 74344
If you pass a singleton list to minNeighborsDistance
then
[]
in the first line, then(x:xs)
assigning the single value to x
and the empty like to xs
, thenhead
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