Home > Software engineering >  Comparing fields of a record
Comparing fields of a record

Time:10-23

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.

  • Related