WTF in Haskell



  • Its my first day of learning Haskell and I just wrote these 2 functions-

    howManyEqual :: Int -> Int -> Int -> Int
    howManyEqual a b c
    	| (a == b) && (b == c)		= 3
    
    	| (a == b) && not(b == c) ||
    	  (b == c) && not(c == a) ||
    	  (c == a) && not(a == b)	= 2
    
    	| not(a == b) &&
    	  not(b == c) &&
    	  not(c == a)			= 0
    
    -- Is this ugly or what!
    howManyOfTwoEqual :: Int -> Int -> Int
    howManyOfTwoEqual a b
    	| (howManyEqual a a b) == 2 	= 0
    	| (howManyEqual a a b) == 3	= 2
    
    

    Now, I don't even know if what I'm doing is WTF-ey or not. Basically what this code does is use a function int howManyEqual(int, int, int) to calculate the function int howManyOfTwoEqual(int, int)!
    So... is this a WTF??? Because I really can't figure out if this is good functional programming or bad! Help me!!!



  • And now I broke the interpreter! I made howManyOfTwoEqual using howManyEqual and howManyEqual using howManyOfTwoEqual in the same script! Call stack overflow in my first script! Beat that!!!

    Now I wanted to have more fun, so I made another function bool allDifferent(int, int, int) that uses howManyEqual and used THAT in howManyEqual... 3 way dependency! Faster stack overflow!
     



  • Hmm that is an interesting implementation indeed :P



  • It has been a long time since I've written anything in Haskell, but could you do something like this:

     

    howManyEqual :: Int -> Int -> Int -> Int
    howManyEqual a a a = 3
    howManyEqual a a b = 2
    howManyEqual a b b = 2

    howManyEqual a b a = 2

    howManyEqual a b c = 0



  • Ooh! Interesting.... But you can't do that. Something similar would work in Prolog though.

    See, with functional programming, the function parameters need to be different, because you are gonna use those parameters inside the function definition, so you just HAVE to have a way to identify which parameter is which one.

    In declarative languages like Prolog, there IS no function 'definition', just the declaration, so code like that one can be used to check if parameters are equal... 



  • Um, what's wrong with

    howManyOfTwoEqual a b
    | a==b = 2
    | a!=b = 0

     

     



  • The evolution of a Haskell programmer.

    "Good" and "Bad" are subjective concepts. Judge for yourself. 



  • A better approach would be to write a single function that can be used for any number of cases.  Passing the arguments as a list is one way to achieve this.  (For example, you might use the "nub" function to remove duplicate elements from the list, then compare its length before and after to count the number of equal values that were removed.)

    Then your functions above would be defined simply as

    howManyEqual a b c = howManyOfListEqual [a, b, c]
    howManyOfTwoEqual a b = howManyOfListEqual [a, b]

    and so on. This gets you the code reuse without relying on hard-coded special cases or ugliness as you are.  :)



  • Patterns are matched top-to-bottom, so if (a == b) && (b == c), the other patterns will be ignored. So there's no need for all those nots:

    howManyEqual a b c
     | a == b && b == c = 3
     | a == b || b == c || a == c = 2
     | otherwise = 0
    

    Also, I might write howManyOfTwoEqual directly:

    howManyOfTwoEqual a b = if a == b then 2 else 0

    By the way, if you use lists you can make howManyEqual work for any number of items, but I don't know if you know enough Haskell yet:

    howManyEqual = maximum . map length . group . sort

    (you'll need to write import Data.List at the top of your source file). Given a list, say [2,3,4,2,4,2], this function sorts the list (giving [2,2,2,3,4,4]), splits it up into groups of equal elements (giving [[2,2,2], [3], [4,4]]), finds the length of each group (giving [3, 1, 2]) and then finds the biggest length (giving 3). But don't worry about all this until you've learned about lists and higher-order functions :)



  • [quote user="nick8325"]

    Patterns are matched top-to-bottom, so if (a == b) && (b == c), the other patterns will be ignored. So there's no need for all those nots:

    howManyEqual a b c  | a == b && b == c = 3  | a == b || b == c || a == c = 2  | otherwise = 0 

    Also, I might write howManyOfTwoEqual directly:

    howManyOfTwoEqual a b = if a == b then 2 else 0

    By the way, if you use lists you can make howManyEqual work for any number of items, but I don't know if you know enough Haskell yet:

    howManyEqual = maximum . map length . group . sort

    (you'll need to write import Data.List at the top of your source file). Given a list, say [2,3,4,2,4,2], this function sorts the list (giving [2,2,2,3,4,4]), splits it up into groups of equal elements (giving [[2,2,2], [3], [4,4]]), finds the length of each group (giving [3, 1, 2]) and then finds the biggest length (giving 3). But don't worry about all this until you've learned about lists and higher-order functions :)

    [/quote]

    howManyEqual only makes sense for 2 or 3 numbers, so using a list would be overkill. Does Haskell have variadic functions?



  • [quote user="Autonuke"]
    howManyEqual only makes sense for 2 or 3 numbers, so using a list would be overkill.
    [/quote]

    Does it? Why's that? It seems to make sense for any amount of numbers to me...howManyEqual should find the size of the biggest equivalence class in a set, in other words the biggest number of same elements which appear in the set.

    Actually, come to think of it, that's not what the original howManyEqual does, because it'll return 0 if all three elements are different, but the list/equivalence-class version will return 1 (since anything is at least equal to itself). IMO 1 is a more sensible answer, but I didn't realise it was different earlier...

    [quote user="Autonuke"]
    Does Haskell have variadic functions?
    [/quote]

    No (strictly speaking it only has functions of one argument, and the rest is currying), but you can emulate them using type classes. That would definitely be overkill, though.



  • [quote user="nick8325"]

    [quote user="Autonuke"]
    howManyEqual only makes sense for 2 or 3 numbers, so using a list would be overkill.
    [/quote]

    Does it? Why's that? It seems to make sense for any amount of numbers to me...howManyEqual should find the size of the biggest equivalence class in a set, in other words the biggest number of same elements which appear in the set.

    Actually, come to think of it, that's not what the original howManyEqual does, because it'll return 0 if all three elements are different, but the list/equivalence-class version will return 1 (since anything is at least equal to itself). IMO 1 is a more sensible answer, but I didn't realise it was different earlier...

    [/quote]

    It could make sense for more than 3 numbers, I guess - e.g. howManyEqual( [1, 1, 2, 3] ). But I don't see what a sensible answer to howManyEqual( [1, 1, 2, 2] ) would be. 

    If it's about the biggest equivalence class, 'howManyEqual' isn't a very good name.



  • [quote user="Autonuke"]
    It could make sense for more than 3 numbers, I guess - e.g. howManyEqual( [1, 1, 2, 3] ). But I don't see what a sensible answer to howManyEqual( [1, 1, 2, 2] ) would be.[/quote]

    I think 2 is a sensible answer - there are two equal things in the list. I don't think it matters much that there are two ways we can find two equal things.

    [quote user="Autonuke"]
    If it's about the biggest equivalence class, 'howManyEqual' isn't a very good name.
    [/quote]

    Well, the equivalence class is equals, so finding the size of the biggest equivalence class means finding out how many elements of the list you can find that are all equal to each other... 



  • [quote user="nick8325"]Well, the equivalence class is equals[/quote]

    Sorry, I meant the equivalence relation is equals. 



  • [quote user="Iago"]

    you might use the "nub" function to remove duplicate elements from the list, then compare its length before and after to count the number of equal values that were removed.

    [/quote]

     

    Now, THAT is elegant! 



  • @Autonuke said:

    [quote user="nick8325"]

    [quote user="Autonuke"]
    howManyEqual only makes sense for 2 or 3 numbers, so using a list would be overkill.

    Does it? Why's that? It seems to make sense for any amount of numbers to me...howManyEqual should find the size of the biggest equivalence class in a set, in other words the biggest number of same elements which appear in the set.

    Actually, come to think of it, that's not what the original howManyEqual does, because it'll return 0 if all three elements are different, but the list/equivalence-class version will return 1 (since anything is at least equal to itself). IMO 1 is a more sensible answer, but I didn't realise it was different earlier...

    [/quote]

    It could make sense for more than 3 numbers, I guess - e.g. howManyEqual( [1, 1, 2, 3] ). But I don't see what a sensible answer to howManyEqual( [1, 1, 2, 2] ) would be. 

    If it's about the biggest equivalence class, 'howManyEqual' isn't a very good name.
    [/quote]

    Well by using Map or IntMap we could return a set of (value -> count) pairs which would yield the number of times a given value is present in the list.

    Using foldl and IntMap would make it fairly easy too.


Log in to reply