-- Time-stamp: -- -- Simple examples from the Haskell intro of F21DP -- See slides at: http://www.macs.hw.ac.uk/~hwloidl/Courses/F21DP/l15_handout.pdf -- -- ----------------------------------------------------------------------------- -- Using the command-line interpreter (ghci): -- Make sure that you have ghc (Glasgow Haskell Compiler) installed on your system -- Run these examples like this: ghci simples.hs -- Now you should see a prompt like this -- #> -- type in Haskell expressions to evaluate them, following the instructions in this file -- In all examples below, #> represents the prompt, so you should just type in the text after the prompt. -- ----------------------------------------------------------------------------- -- Using the web-based IDE: -- If you want to use the web-based IDE at the School of Haskell Web page (https://www.fpcomplete.com/school), -- follow this link: https://www.fpcomplete.com/clone-active-code/VyMbsozGDE?env=ghc-7.8-stable-14.09 -- and replace the Haskell code with the one from these examples. -- You'll find more links to the IDE embedded in the courses on this web site. ----------------------------------------------------------------------------- -- -- Installing ghc: -- On Ubuntu-based systems the following should be sufficient -- sudo apt-get install ghc -- Depending on your setup you may have to enable the 'multiverse' repository first -- To do so, just uncomment this line at the end of /etc/apt/sources.list, by removing the leading '#' -- # deb http://archive.ubuntu.com/ubuntu/ trusty-security multiverse -- -- You are encouraged to run this from inside a docker container, to start with a clean setup and -- an isolate setup changes in that container (see http://www.macs.hw.ac.uk/~hwloidl/Courses/F21DP/docker_notes.txt -- for details) -- ----------------------------------------------------------------------------- -- at the beginning you import code from other modules; -- these are only needed for the monadic examples at the very end import System.Environment(getArgs) -- needed for reading from command line -- import System.Random -- needed for mkRandomList -- NB: for these simple examples you need to avoid name clashes with Prelude (library) -- functions that are automatically imported -- for example, we use 'len' below, to avoid a clash with the existing function 'length' len :: [a] -> Int len [] = 0 len (x:xs) = 1 + len xs -- Observation: the x, bound in the pattern matching is never used on the right-hand-side -- Exercise: how do you change that code to make explicit that the head of the list is not used? -- Example of using len xs = [2,3,5,7,11,13] n = len xs -- NB: if you work in the interpreter, and you want to define a value like n above, type: -- #> let n' = len xs -- Exercise: try that; check that the value of n and of n' are the same -- You can of course use a built-in function that computes the list length; it is called 'length' -- type in the interpreter: -- #> let n'' = length xs -- Exercises: try that; check that the value of n'' is the same as that for n and n' -- More examples of list functions are at the end of the "Haskell Basics" section of the Haskell Intro: -- https://www.fpcomplete.com/school/starting-with-haskell/introduction-to-haskell/1-haskell-basics ----------------------------------------------------------------------------- -- Example of a data-type data MyList a = Nil | Cons a (MyList a) -- Exercise: define mylen, doing the same as 'len' above, but on the type MyList -- mylen :: MyList a -> Int -- mylen ... -- Example of a type synonym type MyIntList = MyList Int -- Example of using this data-type (note that xs' represents the same list as xs) -- The ' in xs' is part of the name; no magic there xs' :: MyIntList xs' = Cons 2 (Cons 3 (Cons 5 (Cons 7 (Cons 11 (Cons 13 Nil))))) -- Exercise: enable the code below, with your definition of mylen (if you do this in the interpreter, use a let) -- n' = mylen xs' -- Example of a tuple, used to encode the first and last-name of a person type Name = String type Person = (String, String) person1 :: Person person1 = ("John", "Smith") -- use either fst and snd to get the components like this fname = fst person1 sname = snd person1 -- or use pattern matching like this get_fname (x,y) = x -- Exercise: define a function for getting the surname -- get_sname ... -- a simple increment fct; note that Integer means arbitrary precision integer numbers (Int is fixed prec) inc :: Integer -> Integer inc n = n+1 -- use it five = inc (inc 3) -- Exercise: what is the expected value and the expected type? -- our initial example fac :: Integer -> Integer fac 1 = 1 fac n = n*fac (n-1) -- another version of factorial, over an interval that is halved in each step of the recursion fact :: Integer -> Integer fact n = fact' 1 n fact' :: Integer -> Integer -> Integer fact' m n | m == n = m | otherwise = (left * right) where mid = (m + n) `div` 2 left = fact' m mid right = fact' (mid+1) n -- another data type (NB: you need the 'deriving' part in order to be able to print and display a value data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving (Show) -- the next day function next :: Day -> Day next Mon = Tue next Tue = Wed next Wed = Thu next Thu = Fri next Fri = Sat next Sat = Sun next Sun = Mon -- You can find a lot more examples in the "Algebraic Datatypes" section of the Haskell Intro: -- https://www.fpcomplete.com/school/starting-with-haskell/introduction-to-haskell/2-algebraic-data-types ----------------------------------------------------------------------------- -- tree examples -- see tree.hs for a definition of Tree a and fringe -- follow the instructions in this file to do the exercises ----------------------------------------------------------------------------- -- more list examples -- string examples hi = "hello" ++ (' ' : ['w','o', 'r', 'l', 'd']) -- NB: a string is just a list of characters; the above concatenates 2 strings -- take the first n elems; again, beware of a name clash with the prelude function mytake 0 _ = [] mytake _ [] = [] mytake n (x:xs) = x : mytake (n-1) xs -- Exercise: use this definition to extract just the "hello" part from the string hi above -- guarded patterns: you can define conditions that need to be true for one branch to match sign x | x > 0 = 1 | x == 0 = 0 | x < 0 = -1 ----------------------------------------------------------------------------- -- for different variants of the sum-of-squares running example, see sqs.hs ----------------------------------------------------------------------------- -- a sorting function; simples! quicksort [] = [] quicksort (x:xs) = quicksort [y | y <- xs, y=x] -- Exercise: test it on a list of values from 9 down to 1 -- #> quicksort [9,8..1] -- Exercise: use the mkRandList function below to generate a list of random values and then sort it -- higher-order fcts add x y = x + y -- alternative definition of inc (see above) -- inc :: Integer -> Integer -- inc = add 1 ys = map inc [1..10] -- Exercise: what is the result of evaluating ys? -- infinite data structures numsFrom n = n : numsFrom (n+1) squares = map (^2) (numsFrom 0) zs = take 5 squares -- Exercise: what is the result of evaluating zs -- this is a more verbose version of squares, working on a list verbose_squares :: [Integer] -> [Integer] verbose_squares xs' = if (null xs') then [] else let x = head xs' xs = tail xs' in x^2 : verbose_squares xs -- try this: -- #> verbose_squares [1..10] -- see sqs.hs for more examples of the sum-of-squares function sqs_even xs = [ x^2 | x <- xs, even x] -- Example: Sieve of Erathostenes ------------------------------------------------------- -- iteratively remove all multiples of identified prime numbers sieve :: [Integer] -> [Integer] sieve (p:xs) = p : sieve (removeMults p xs) -- remove all multiples of a given number removeMults :: Integer -> [Integer] -> [Integer] removeMults p xs = [ x | x <- xs, not (x `rem` p == 0) ] -- define an infinite list of prime numbers -- beware, this is an infinite list primes :: [Integer] primes = sieve [2..] -- sieve (p:xs) = p : sieve (removeMults p xs) -- removeMults p xs = [ x | x <- xs, not (x `rem` p == 0) ] -- Exercise: use the definition of primes to print all prime numbers less than 100 ----------------------------------------------------------------------------- -- monadic code to read arguments from the command-line -- needs this import at the beginning of the file -- import System.Environment(getArgs) -- then, define a main function like this (beware of indentation!) {- -- use this as the main function in a separate file, to read 2 values from the command line and process them main = do args <- getArgs -- read from commandline let x = read (args!!0) -- first argument let y = read (args!!1) -- second argument putStrLn (show (sqs_even [x..y])) -- process the input -} {- -- monadic code, and I/O operations myAction :: String -> IO Int -- define an IO-action, that takes a string as input myAction fn = do -- this starts a block of monadic actions str <- readFile fn -- perform an action, reading from file let lns = lines str -- split the file contents by lines let nums = map read lns -- turn each line into an integer value let res = sum nums -- compute the sum over all integer values print res -- print the sum return res -- return the sum writeSample :: String -> Int -> Int -> Int -> IO () writeSample fn low high len = do xs <- mkRandList low high -- generate a list of random numbers writeFile fn (unlines (map show (take len xs))) -- write the list to file, line by line seed = fromIntegral (primes!!99) -- my favourite prime number -- generate a list of random numbers -- needs: import System.Random mkRandList :: Int -> Int -> IO [Int] mkRandList low high = do let g = mkStdGen seed setStdGen g let xs = randomRs (low,high) g -- generate an infinite list of random numbers return xs -- to test this code, type the following in ghci -- #> writeSample "numbers.txt" 1 65535 99 -- -- this generates a file "numbers.txt" with 99 lines, each containing a value btw 1 and 65535 -- #> myAction "numbers.txt" -- -- this compute the sum over all numbers and print the result -- Exercise: try the above! -- you can create your own file, called "numbers.txt", to test different input -- then call myAction from inside ghci like this -- #> myAction "numbers.txt" -- Exercise: what should be printed? ----------------------------------------------------------------------------- -- Example: Caesar Cipher -- for the caesar cipher example, see http://www.macs.hw.ac.uk/~hwloidl/Courses/F21CN/Labs/CryptoI/caesar.hs ----------------------------------------------------------------------------- -} -- end of file