0

I'm trying to solve the Mini-Max Sum from HackerRank using Haskell. I'm just getting started with Haskell. Watched video's from #tsoding (youtube channel) and got inspired with his one line solutions to the problems.

I'm trying to provide a one-line solution to this problem. I'm following the 2nd approach from this youtube video:

  1. Find the minimum element in the array;
  2. Find the maximum element in the array;
  3. Calculate the sum of all the elements in the array;
  4. Calculate minimum by subtracting max from sum; and
  5. Calculate maximum by subtracting min from sum.

This is what my code looks like so far:

main = interact $ show . foldr (-) 0 [minimum, maximum, sum] . map read . words

foldr (-) 0 [minimum, maximum, sum] (here is it possible to get the sum of numbers to be piped for foldr function?)

Even though my current code is wrong, I'm imagining the solution would look something like this. Could someone please help me with the syntax if this is possible?

MikaelF
  • 3,518
  • 4
  • 20
  • 33
tuxian
  • 159
  • 5
  • 12
  • 1
    What exact problem with syntax you have? I'm not sure what you want to achiev with `foldr (-) 0 [minimum, maximum, sum]`. It is equivalent of `minimum - (maximum - (sum - 0))` which doesn't make any sense for me. – talex Mar 13 '20 at 08:12
  • I'm trying to achieve ((sum - 0) - minimum) and ((sum-0) - maximum) separately. sorry for the confusion. In the youtube video link attached, I'm trying to solve this problem using 2nd approach. – tuxian Mar 13 '20 at 08:19
  • 1
    What do you mean by `sum - 0`? It doesn't typechecks. `-` have type `Num a => a -> a -> a`. So `sum` doesn't fit in it. – talex Mar 13 '20 at 08:28
  • ok, so looks like i cannot solve this with one liner. I understood the issue here. Thanks @talex – tuxian Mar 13 '20 at 11:59

1 Answers1

2

The main issue in your code (and I think also what you were trying to solve) is that you're trying to collapse a list of functions by combining them with (-) :: Num a => a -> a -> a, which doesn't really make sense. If I understand correctly, what you want is to apply a single value (the input list) to every function in a list. There are multiple ways to do this, as explained in the answers to this recent SO question. With this simple trick, you're almost there. Here's the rest of the algorithm.

For brevity's sake, I will use the following input list:

$> let list = [1,2,3,4,5]

Using one of the methods from the linked SO question, you can feed this to every function in the list:

$> sequenceA [maximum, minimum, sum] list
$> [5,1,15]

Then you can pattern match on the list to get the values you want:

$> let [ma, mi, su] = sequenceA [maximum, minimum, sum] list in [su - ma, su - mi]
$> [10,14]

One inconvenient of this let-in construct is that it doesn't allow for eta-reduction of the list variable, so you will either have to use a lambda or a where clause. For a function this long, I think a lambda isn't very readable, so I prefer a where clause:

main = interact $ unwords . map show . f . map read . words
  where f xs = let [ma, mi, su] = sequenceA [maximum, minimum, sum] xs 
               in  [su - ma, su - mi]

I modified your main function a bit, since what you want to print out is not the list show list, but every number in the list, separated by spaces: unwords $ map show list. You can also think of this as the opposite of what you had done to get the list in the first place (map read . words).

Because this involves pattern-matching, I think it works better as a multi-line function, but there might be ways to solve it as a one-liner (other than making f a long and ugly lambda).

EDIT

Here is a one-liner which uses arrows (from Control.Arrow) and the Applicative instance of -> to feed the list to steps 4 and 5, and therefore avoid the long lambda:

main = interact $ unwords . map show . (\(a,b) -> [a,b]) . 
       ((-) . sum <*> maximum &&& (-) . sum <*> minimum) . 
       map read . words

There is still some pattern-matching involved (to convert the tuple produced by &&& into a list), and I don't think it's more readable. Maybe there's a better way, but I don't really see it.

MikaelF
  • 3,518
  • 4
  • 20
  • 33
  • Thank you so much @MikaelF.. This is exactly what i'm looking for. Thank you so much for taking time to update my question and also for providing solution to that. – tuxian Mar 25 '20 at 17:16
  • could you please explain me the approach you have added at the end? – tuxian Mar 25 '20 at 18:58
  • You can look at some pretty good explanations for this specific usage of `<*>` [here](https://stackoverflow.com/questions/11810889/functions-as-applicative-functors-haskell-lyah), as well as [here](https://stackoverflow.com/questions/4191424/what-are-arrows-and-how-can-i-use-them) for the fanout operator (`&&&`) from the Arrow typeclass. That kept me busy for a while when I first learned about it. – MikaelF Mar 25 '20 at 19:03