I've been learning Haskell from LYAH for quite some time, and as a first practice for Chapter 7: Modules, I decided to make a simple numTimesFound
function: it returns the number of times an element is found in a list.
Here's my working code:
numTimesFound :: Ord a => a -> [a] -> Integer
numTimesFound _ [] = 0
numTimesFound x list
| x `notElem` list = 0
| otherwise =
let
appearencesTable = map (\full@(first:_) -> (first, length full + 1)) . group . sort $ list
appearencesOfX = snd $ head $ filter (\(elem, _) -> elem == x) appearencesTable
in
toInteger appearencesOfX
Personally, I think I take the cake for using Haskell features, but I also think that the intent of this code is hard to understand. One of my major gripes with it is that it's hard to tell what the appearencesTable
actually is from the code - it's a list of tuples that gives you the number of times each element was found. For example, for the list [1, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 5]
the appearencesTable
is [(1, 3), (2, 2), (3, 4), (4, 2), (5, 1)]
. If you know that it's pretty easy to get what the rest of the code is trying to do, but it still does require quite a bit of study for such a trivial function.
I would greatly appreciate any help in improving the readability of this code.
-
\$\begingroup\$ For those who don't know, "LYAH" is Learn You A Haskell for Great Good \$\endgroup\$Jeel Shah– Jeel Shah2016年08月25日 14:57:51 +00:00Commented Aug 25, 2016 at 14:57
1 Answer 1
Since your type a is required to be an Ord you can access Eq functions. This makes counting occurrences a bit simpler, because you can just filter and count the matching elements:
numTimesFound :: Ord a => a -> [a] -> Integer
numTimesFound _ [] = 0
numTimesFound x list = sum $ map (\a -> 1) $ filter (== x) list
Of course we don't need to stop there. We can use function composition and length
to write the last line as:
numTimesFound x xs = (length . filter (== x)) xs
In addition to that 200_success raises an interesting point in the comments:
Actually, there is no reason to require
Ord a
, whenEq a
will do
-
\$\begingroup\$ Man that was simpler than I thought! I guess the style of thinking has to be pretty different to write readable code in Haskell. Thanks so much! \$\endgroup\$naiveai– naiveai2016年08月25日 07:28:14 +00:00Commented Aug 25, 2016 at 7:28
-
\$\begingroup\$ Note that you don’t need the explicit case to handle the empty list at all;
count
will happily return0
if the list is empty. \$\endgroup\$Alexis King– Alexis King2016年08月25日 07:43:17 +00:00Commented Aug 25, 2016 at 7:43 -
1\$\begingroup\$ Wait, I just realized - the
count
function is the same aslength
, so I can just write my original function asnumTimesFound x = length . filter (==x)
! I am so dumb! \$\endgroup\$naiveai– naiveai2016年08月25日 07:44:03 +00:00Commented Aug 25, 2016 at 7:44 -
4\$\begingroup\$ Actually, there is no reason to require
Ord a
, whenEq a
will do. \$\endgroup\$200_success– 200_success2016年08月25日 08:55:40 +00:00Commented Aug 25, 2016 at 8:55 -
\$\begingroup\$ @200_success: Yes, that is true. The only reason I was using
Ord
originally was because I usedsort
. \$\endgroup\$naiveai– naiveai2016年08月25日 13:56:26 +00:00Commented Aug 25, 2016 at 13:56