Tsoding
HackerRank in Haskell – HaskellRank #01 - YouTube

Imperative style (wrong)

Haskell is about declarative programming, not imperative.

1
2
10
5

1
2
3
4
5
6
7
8
solveMeFirst a b = a + b

main :: IO ()
main = do
     val1 <- readLn
     val2 <- readLn
     let sum = solveMeFirst val1 val2
     print sum
15

interact function

Takes a function that takes string and returns a string and applies a side-effect to the output (typically, printing to screen).

1
2
3
:t interact
-- You can write interactive haskell programs in a declarative way
-- interact :: (String -> String) -> IO ()
interact :: (String -> String) -> IO ()

dollar operator

1
:t ($)
($) :: (a -> b) -> a -> b

We will use this operator to separate interact from its argument.

Declarative style (correct)

1
1 2

1
main = interact $ show . sum . map read . words
3

Explanation

1
main = interact $

The words function takes a single string and splits it up into tokens (a list of strings).

1
:t words
words :: String -> [String]
1
2
3
4
5
6
7
8
-- This wont compile because interact expects
-- a function that returns a string, but words
-- returns a list of strings
-- main :: IO ()
-- main = interact $ words "foo bar baz"

main :: IO ()
main = interact $ words "foo bar baz"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/tmp/file_tempfile_4GR2qL_rand-26110_pid-18248.hs:8:19: error:
  • Couldn't match expected type ‘String -> String’
                with actual type ‘[String]’
  • Possible cause: ‘words’ is applied to too many arguments
    In the second argument of ‘($)’, namely ‘words "foo bar baz"’
    In the expression: interact $ words "foo bar baz"
    In an equation for ‘main’: main = interact $ words "foo bar baz"
|
8 | main = interact $ words "foo bar baz"
|                   ^^^^^^^^^^^^^^^^^^^

Troubleshooting

1
words "foo bar baz"

/tmp/file_tempfile_4xJeY1_rand-14847_pid-15518.hs:1:1: error:
    Parse error: module header, import declaration
    or top-level declaration expected.
  |
1 | words "foo bar baz"
  | ^^^^^^^^^^^^^^^^^^^
1
words "foo bar baz"
["foo","bar","baz"]

A top-level declaration is something like main =.

1
2
3
main =
    print (fst (1,2)) >>
    print (snd (1,2))

Array sum

hs d
words read tokens
tail remove the first element
map read convert string elements to numbers
sum take the sum
show convert back to string
interact connect the algorithm to stdin and stdout

1
2
6
1 2 3 4 10 11

1
main = interact $ show . sum . map read . tail . words
31

A very big sum

The type Integer has arbitrary precision.

It automatically scales when the sum grows.

1
:info Integer
data Integer
  = integer-gmp-1.0.2.0:GHC.Integer.Type.S# GHC.Prim.Int#
  | integer-gmp-1.0.2.0:GHC.Integer.Type.Jp# {-# UNPACK #-}integer-gmp-1.0.2.0:GHC.Integer.Type.BigNat
  | integer-gmp-1.0.2.0:GHC.Integer.Type.Jn# {-# UNPACK #-}integer-gmp-1.0.2.0:GHC.Integer.Type.BigNat
  	-- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
instance Eq Integer
  -- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
instance Ord Integer
  -- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
instance Show Integer -- Defined in ‘GHC.Show’
instance Read Integer -- Defined in ‘GHC.Read’
instance Enum Integer -- Defined in ‘GHC.Enum’
instance Num Integer -- Defined in ‘GHC.Num’
instance Real Integer -- Defined in ‘GHC.Real’
instance Integral Integer -- Defined in ‘GHC.Real’

1
2
5
1000000001 1000000002 1000000003 1000000004 1000000005

1
main = interact $ show . sum . map read . tail . words
5000000015

Grading students

1
:t mod
1
12 `mod` 5
2
mod :: Integral a => a -> a -> a

1
2
3
4
5
4
73
67
38
33

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- The number we need to get to the next multiple of 5
needfornext5 :: Int -> Int
needfornext5 x = 5 - x `mod` 5

-- m5 = the next multiple of 5
-- | is a condition
round5 :: Int -> Int
round5 x
       | x < 38 = x
       | (m5 - x) < 3 = m5
       | otherwise = x
       where m5 = x + (needfornext5 x)

main = interact $ unlines . map (show . round5 . read) . tail . words
75
67
40
33

Collapsed a condition

1
2
3
-- unlines takes a list of strings and returns a single string
-- concatenates all strings, separating with newlines
:t unlines
unlines :: [String] -> String
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- The number we need to get to the next multiple of 5
needfornext5 :: Int -> Int
needfornext5 x = 5 - x `mod` 5

-- m5 = the next multiple of 5
-- | is a condition
round5 :: Int -> Int
round5 x
       | x >= 38 && (m5 - x) < 3 = m5
       | otherwise = x
       where m5 = x + (needfornext5 x)

main = interact $ unlines . map (show . round5 . read) . tail . words
75
67
40
33

Can be written like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
-- The number we need to get to the next multiple of 5
needfornext5 :: Int -> Int
needfornext5 x = 5 - x `mod` 5

-- m5 = the next multiple of 5
-- | is a condition
round5 :: Int -> Int
round5 x
       | x >= 38 && (m5 - x) < 3 = m5
       | otherwise = x
       where m5 = x + (needfornext5 x)

solve :: [Int] -> [Int]
solve xs = map round5 xs

main = interact $ unlines . map show . solve . map read .tail . words
75
67
40
33