1
votes

I'm trying to write a function in Haskell that takes 4 integers as input and counts how many of that input numbers are equal to the first integer.

For example:

howManyEq 3 4 5 6   returns 0
howManyEq 3 4 3 5   returns 2
howManyEq 3 4 3 3   returns 3
howManyEq 3 3 3 3   returns 4

This is the function I have created so far, it works for the first instance when there is no input matching the first integer. But it doesn't count the first 3 when there are other matching integers, it always has one less counted.

howManyEq :: Int -> Int-> Int -> Int -> Int
howManyEq a b c d = count (head (toList a b c d)) (tail (toList a b c d))

count :: Int -> [Int] -> Int
count x [] = 0
count x (y:ys)
 | x == y = 1 + (count x ys)
 | otherwise = count x ys

toList :: Int -> Int-> Int -> Int -> [Int]
toList a b c d = [a,b,c,d]

My function gives me:

howManyEq 3 4 5 6   returns 0
howManyEq 3 4 3 5   returns 1
howManyEq 3 4 3 3   returns 2
howManyEq 3 3 3 3   returns 3

I'm not sure how to count the first integer when the other input integers are equal.

2
Given your logic, howManyEq can never return 1. Create function that returns + 1 to what you're returning now. Feed that result into a function that is defined as f 1 = 0 and f a = a...Dair
Wel is your example not incorrect? Shouldn't the first return 1. If that's the case, this is a typical off by one error.Willem Van Onsem
howManyEq 3 4 3 5 should return 2 because it counts both instances of 3. But howManyEq 3 4 5 6 should return 0. I know its kinda illogical, but this is the way my teacher wants it for some reason.T-Bird

2 Answers

1
votes

Your count function counts the number of equal elements in the remainder of the "list". If that number is zero, your teacher wants the result to be zero. If there is one or more, it returns the amount counted in the remainder of the list, but since there is already one element in the head of the list, you should do +1. You can do this by implementing a helper function:

fix :: (Eq a, Num a) => a -> a
fix 0 = 0
fix n = n+1

Now you can simply pass the result of your count function through fix:

howManyEq :: Int -> Int-> Int -> Int -> Int
howManyEq a b c d = fix $ count (head (toList a b c d)) (tail (toList a b c d))

count :: Int -> [Int] -> Int
count x [] = 0
count x (y:ys)
 | x == y = 1 + (count x ys)
 | otherwise = count x ys

toList :: Int -> Int-> Int -> Int -> [Int]
toList a b c d = [a,b,c,d]

additional advice:

  • You use head (toList a b c d) in your call to count. But you already know in advance that this is a, so omit this.

  • The same with the tail: simply only feed the last three arguments, so: toList b c d.

  • Use the most generic type signature possible: for toList :: a -> a -> a -> [a] (given the previous fix), for count :: (Eq a, Num b) => a -> [a] -> b, and for howManyEq :: (Eq a, Eq b, Num b) => a -> a -> a -> a -> b.

  • If you define the same thing multiple time, you can use a where t = ... clause, like where cys = count x ys

This would result in something like:

fix :: (Eq a, Num a) => a -> a
fix 0 = 0
fix n = n+1

howManyEq :: (Eq a, Eq b, Num b) => a -> a -> a -> a -> b
howManyEq a b c d = fix $ count a $ toList b c d

count :: (Eq a, Num b) => a -> [a] -> b
count x [] = 0
count x (y:ys)
 | x == y = 1 + cys
 | otherwise = cys
 where cys = count x ys

toList :: a -> a -> a -> [a]
toList a b c = [a,b,c]
-1
votes

Here's one way:

{-# LANGUAGE TypeFamilies #-}

class HowManyEq a where
  howManyEq' :: Int -> Int -> Int -> a

instance HowManyEq Int where
  howManyEq' count x y = count + fromEnum (x == y)
  {-# INLINE howManyEq' #-}

instance (HowManyEq a, b ~ Int) => HowManyEq (b -> a) where
  howManyEq' count x y b = (howManyEq' $! count + fromEnum (b == x)) x y
  {-# INLINE howManyEq' #-}

howManyEq :: HowManyEq a => Int -> Int -> a
howManyEq = howManyEq' 0

howManyEq4 :: Int -> Int -> Int -> Int -> Int
howManyEq4 x y z w =
  case howManyEq x y z w of
    0 -> 0
    n -> n + 1