I have the record lettersandnumbers (Lan), with a field for letters and a field for numbers:
data Lan = Lan {letter :: String, number :: Int}
Furthermore i have the type
type Lanlist = [Lan]
Now, i want to write a function, that given a Lanlist, returns True if all elements have the same number. So it would be something like this:
equalNumber :: [Lan] -> Bool
The problem is: i don't know how to simply access the corresponding field of the number record. I tried
equalNumber xs = map f xs
, where i don't know what f is supposed to be (something that only checks the number field with the previous one, for example). Or maybe i could use the and or the filter function. Many possibilities, but the fact that it is in a record confuses me. Someone that can help?
CodePudding user response:
The way your question is phrased strongly suggests you simply lack the knowledge on how to work with records.
Given
data Lan = Lan {letter :: String, number :: Int} deriving Show -- (*)
(*) I took the liberty to derive Show, it's a convenient
When you create a record in Haskell it gives you construtor, in this case Lan; and also accessors, in this case letter
and number
. Let's have a look of this in action:
λ> data Lan = Lan {letter :: String, number :: Int} deriving Show
-- construction. Two ways to do it.
λ> lan = Lan "A" 5
λ> lan' = Lan { letter = "B", number = 10 }
λ> lan
Lan {letter = "A", number = 5}
λ> lan'
Lan {letter = "B", number = 10}
-- The accessors
λ> letter lan
"A"
λ> letter lan'
"B"
λ> number lan
5
λ> number lan'
10
-- we can also patter match
λ> plus1 (Lan { number = n }) = n 1
-- ^ if it's a record we can name the fields in the pattern
-- Regular pattern matching
λ> plus1' (Lan _ n) = n 1
λ> plus1 lan
6
λ> plus1' lan
6
In vanilla Haskell this is all there is to knwo about records.
Now for the problem at hands. We have a record containing a letter and number and we want to determine if a list of Lans have all the same numbers. Clearly we can discard letters. Let's start with that:
λ> numbers xs = map number xs
λ> :t numbers
numbers :: [Lan] -> [Int]
λ> lans = [lan, lan']
λ> lans' = [Lan "Z" 15, Lan "X" 15]
λ> numbers lans
[5,10]
λ> numbers lans'
[15,15]
Now that we have a list of numbers we can figure out how establish if they are all equal.
λ> eqs xs = and $ zipWith (==) xs (tail xs)
λ> eqs (numbers lans')
True
λ> eqs (numbers lans)
False
In conclusion:
equalNumber :: [Lan] -> Bool
equalNumber = eqs . numbers
CodePudding user response:
No need to do everything in a single map
. You can start with simply transforming the list of Lan
into a list of plain numbers
map number xs
...which is all that's needed for determining whether the property holds. You'll probably want to use it something like this:
equalNumber xs = case map number xs of
[] -> ...
(n:ns) -> ...
CodePudding user response:
To access a record you just use the name of the function.
In this case it would be number
. So having:
numbers xs = map number xs
will give you a list of all the numbers inside Lan
objects.
Ideally I would not put everything inside the map. Following is an example of how the comparison could be implemented.
equalNumber :: [Lan] -> Bool
equalNumber xs = and [x == y | (x, y) <- zip numbers (tail numbers)] where
numbers = map number xs
What the code above does is, it creates a list of the numbers in the list of Lan
objects, creates a list of tuples of adjacent numbers, and compares them to make sure all of them are equal.