CS 291 University of Puget Sound Spring, 2020
 
Programming Language Paradigms:
Explorations with Functional Problem Solving (supported by Scheme/Haskell)
and Declarative Prblem Solving (supported by Prolog)
 

Haskell Worksheet 1, due Monday, February 24

Table Comparing Scheme and Haskell Basics

  Scheme Language Haskell
Topic Syntax Example Comparison(s) Syntax Example
starting environment (after installation) scheme typed in command line after installation, Haskell requires creating each new project for a new project, you need to
  • create a new directory,
  • download relevant parts of the compiler,
  • build a minimal project
  • start running
for a new project projectName (note: no dashes allowed in programName),
type in a terminal window:
   stack new projectName
   cd project-name
   stack setup
   stack build
   stack ghci
load file (load "—") (load "filename.ss") slightly abbreviated in Haskell :l filename, where stored file is filename.hs in the project directory
(here: l is a lower case L)
:l filename
comments comments start with a semicolon and continue to the end of the line (f x) ; comment for f(x) Scheme and Haskell allow single-line comments;
only Haskell allows multi-line comments
single-line comments start with two dashes – –
multiple-line comments start with {- and end with -}
          {- this is a
             multi-line
             comment -}
        
variable typing variables can have any type, and types can be mixed (e.g., in a list) (1 "two" [3 4] ()) Scheme flexible; Haskell not types inferred or explicitly declared; once determined cannot change [1, 2, 3, 4] ok
[1, "two", [3, 4], [ ] ] not ok
numbers Operators on mixed types change integers to fractions to reals (/ 2 5) yields fraction 2/5 by default, numbers have type Num Real, Rational, Integer types available
For conversions, see Tutorial 10 Numbers, section 10.3
often negative numbers should be written in parentheses
2 / 5 yields real number 0.4
(-7) denotes negative 7
Boolean types and operators
  • Values: #t, #f
  • Common operators: not, and, or, <, >
  Haskell expands Scheme's list of operators (not (< a b c))
  • Values: True, False
  • Common operators: &&, ||, and or, not , all, any, elem, notElem (elem and notElem indicate elements in a list or not)
notElem example:
[x | x <- "abcdefghijklmnopqrstuvwxyz", x `notElem` "aeiou" ]
arithmetic operations +, -, *, /
arguments must be numbers
n-ary operations use prefix notation
(+ 2 3 4)
different notational systems used for expressions operations +, -, *, /
arguments must be numbers
  • binary operations use infix notation
    (2 + 3) + 4
  • additional operators available (prefix notation)
    • min: minimum
    • max: maximum
    • gcd: greatest common divisor
    • lcm: lowest common multiple
    • div: integer division
    • mod: remainder after integer division
lists use parentheses with spaces (2 3 4 5) and ("one" "two" "three") lists built in to both languages use square brackets with commas [2, 3, 4, 5] and ["one", "two", "three"]
primitive list extractors car, cdr (car '(1 2)) yields 1 car equivalent to head; cdr equivalent to tail; head, tail, last (return last element), init (return all but last) init [1, 2, 3] yields [1, 2]
primitive list constructor cons (cons 3 '(5 4 6)) ===> (3 4 5 6) mostly a change in format, although Haskell lists may contain only elements of the same type   : operator 3 : [4, 5, 6] ===> [3, 4, 5, 6]
both Scheme and Haskell have extensive list operators See Scheme lists Many operations are equivalent, but sometimes with different names See Data.List
defining lists list elements are placed in parentheses (often with an initial quote) '(3 1 4 1 5 9 2) Haskell extends options available in Scheme
  • specify specific values using square brackets and commas
  • (Texas ranges, version 1)specify starting and ending numbers, separated by .. to indiate sequence increasing by 1
  • (Texas ranges, version 2) specify first, second, and last numbers, with .. before the last, for an increasing sequence in arithmetic progressing based on the difference between the first two.
  • (list comprehensions) specify a (potential infinite) list of numbers, selecting all numbers in a range that satisfy one or more conditions
  • (constrained list comprehensions) take a specified number of values from the start of a list comprehension.
   [1 .. 7]  ===> [1, 2,3, 4, 5, 6, 7]
   [1, 3 .. 7] ===>[1, 3, 5, 6]
   [-3 .. 3] ===> [-3, -1, 1, 3]
   [x | x <- [1 .. ], (mod x 2) == 0]
         ===> list of all positive even integers
   take 7 [x | x <- [1 .. ], (mod x 2) == 0]
         ===> [2,4,6,8,10,12,14]
          
flexible typing versus strongly typed variables can have any type, and types can be mixed (e.g., in a list) (1 "two" [3 4] ()) Scheme flexible; Haskell not types inferred or explicitly declared and cannot change [1, 2, 3, 4] — ok
[1, "two", [3, 4], [ ] ] — not ok
strings strings are a specified type "this is a string" Strings in Haskell not a separate type String in Haskell considered a list of characters "Computer" is considered ['C', 'o', 'm', 'p', 'u', 'p', 't', 'e', 'r']
built-in functions extensive library call with parentheses and prefix notation: (func-name parameters) consistent prefix notation in Scheme; prefix notation without parentheses for named functions in Haskell, but infix notation for arithmetic extensive library call with func-name parameters (no parentheses): f x y
function definitions start with (define func-name (lambda (parameters) ... (define f (lambda (x y) same concepts, Haskell's syntax is somewhat simpler start with funcName parameters =
initial word let usually optional
separate name and parameters by spaces
f x y =
Note: if f is a user-defined, two-parameter function, then it may be called in prefix form f x y or as an infix operator (if used with backquotes) x `f` y
if-statements Basic syntax with parentheses:
   (if
       then-action
       else-action)
          
else optional in Scheme, not in Haskell; if else-action in Scheme, both can be used within more extensive expressions Basic syntax with then, else—must always return something
   if (condition)
      then then-action
      else else-action
        
bindings use define for globals; let, let*, letrec for locals (define pi 3.1415926535) within a function, once bound, values cannot change let often optional in definitions pi = 3.1415926535
tuples (in Haskell) not explicitly available, although lists could be used Scheme lists can contain different types and can be of different lengths
Haskell tuples can contain different types, but each tuple must have the same length.
See the reading on Tuples

Two examples for determining the length of a list.

(This is provides an implementation for the length functions in both Scheme and Haskell.)


    (define mycount
       (lambda (lst)
           (mycount-kernel 0 lst)
    )  )
    (define mycount-kernel
       (lambda (count-so-far ls)
          (if (null? ls)
                count-so-far
                (mycount-kernel (+ 1 count-so-far) (cdr ls))
    )  )  )
  myCount lst = myCountKernel 0 lst

  myCountKernel countSoFar ls =
     if (ls == [ ])
        then countSoFar
        else myCountKernel (1 + countSoFar) (tail ls)

Problems for this Worksheet 1 with Haskell

This worksheet marks a transition in the course from working with functional problem solving with Scheme to functional problem solving with Haskell. Thus, this lab considers problem done previously in Scheme. For this lab,

  1. Write a function to implement f(x) = x2 - 3x + 2, and evaluate the function for x = 0, 1, 2, 3, 4.

  2. Define a procedure that computes the volume of a sphere, given its radius.

  3. Define a procedure that, given the radius of a circle, returns its circumference and area on a list.

  4. Write a function classify that has a number as a parameter and returns the appropriate string: "negative", "zero", or "positive".

  5. Write a function byTwos that with two parameters (a starting and ending value) and returns a list of numbers beginning at the starting number and progressing in increments of 2 until the ending value is exceeded.

       byTwos  1 11 ===> [1, 3, 5, 7, 9, 11]
       byTwos  1 10 ===> [1, 3, 5, 7, 9]
       byTwos  2 9 ===> [2, 4, 6, 8]
       byTwos (-7) 7 ===> [-7, -5, -3, -1, 1, 3, 5, 7]
            

    Note: this can be done directly in Haskell, no need for recursion!

       classify -7 ===> "negative"
       classify 0  ===> "zero"
       classify 3.141592 ===> "positive"
        
  6. Define Texas ranges for the following:

          [(-2) .. 5]
          [-1 .. 5]
          [(-1.3) .. 3.3]
          [2.5 .. 7.7]
          [2.5, 3 .. 6]
          [2.5, 3, 3.5 .. 6]
          [6 .. 2]
        

    In each case, indicate what result is obtained, and briefly explain why.

  7. Explain what the following function does and explain why.

          niceList value = (take value (cycle [3, 1, 4, 1, 5]))
        
  8. Consider the following function.

          niceList value = (take (5*value) (repeat value))
        
    1. Describe what this function does when given a numeric parameter, and explain why this result is returned.
    2. What happens if this function is given a string or a letter as its parameter, and explain briefly.
  9. Write a function myRepeat with parameter value that returns a list with value copies of 1, 2, 3, ..., value

       myRepeat 1  ===> [1]
       myRepeat 3  ===> [1, 2, 3, 1, 2, 3, 1, 2, 3]
       myRepeat 0  ===> [ ]
          
  10. Consider multiples of either 11 or 13.

    1. Using the idea of a list comprehension, write an expression that defines an infinite list of numbers that are multiples or 11 or 13.
    2. Using your your answer to part a, define a function multiples11Or13 which has an integer parameter num and returns a list of the first num multiples of 11 or 13.
        multiples11Or13  1 ===> [11]
        multiples11Or13  2 ===> [11, 13]
        multiples11Or13  5 ===> [11, 13, 22, 26, 33]
              
  11. Write a function identifyConsonants that takes a string as parameter and returns a string of all uppercase and lowercase consonants.

            identifyConsonants "This is A TEsT" ===> "ThssTsT"
          

    Hint: Look at the examples in the reading on list comprehensions.

Extra Credit Problem

  1. Computing a Polynomial

    A polynomial function has the form

    p(x) = anxn + an-1xn-1 + ... + a2x2 + a1x + a0

    Write a Haskell function computePoly that takes two parameters:

    and returns the value of the polynomial p(x).

    Notes:


    created 8 February 2020
    revised 10 February 2020
    Valid HTML 4.01! Valid CSS!
    For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.