CS374: Programming Language Principles - Programming Paradigms

Activity Goals

The goals of this activity are:
  1. To define the imperative, declarative (functional and logic), object-oriented, and scripting programming paradigms
  2. To explore concurrency within programming paradigms

Supplemental Reading

Feel free to visit these resources for supplemental background reading material.

The Activity

Directions

Consider the activity models and answer the questions provided. First reflect on these questions on your own briefly, before discussing and comparing your thoughts with your group. Appoint one member of your group to discuss your findings with the class, and the rest of the group should help that member prepare their response. Answer each question individually from the activity, and compare with your group to prepare for our whole-class discussion. After class, think about the questions in the reflective prompt and respond to those individually in your notebook. Report out on areas of disagreement or items for which you and your group identified alternative approaches. Write down and report out questions you encountered along the way for group discussion.

Model 1: Imperative Languages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
    public static void main(String[] args) {
        int a = 1, b = 2, c = 1;
         
        float discriminant = Math.sqrt(4.0*a*c) / 2*a;
         
        float root = -b + discriminant;
         
        System.out.println(root);
         
        root = -b - discriminant;
         
        System.out.println(root);
    }
}

Questions

  1. What defines a statement?
  2. What, in general, does a statement realy do?
  3. At what points do the value of root change?
  4. What would happen if a is 0?
  5. How can we represent this program using an Object-Oriented style? What state might the object represent?

Model 2: The C Programming Language and Memory Management

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
 
struct Person {
    char* name;
    int age;
    float salary;
};
 
void tax(struct Person* p) {
    if(p->salary > 100000) {
        p->salary = p->salary * 0.85;
    } else {
        p->salary = p->salary * 0.95;
    }
}
 
int main(void) {
    struct Person* bill = (struct Person*) malloc(sizeof(struct Person));
    bill->name = (char*) malloc(strlen("Bill")+1);
    strncpy(bill->name, "Bill", strlen("Bill")+1);
    bill->age = 38;
    bill->salary = 20000;
    tax(bill);
    printf("%s (%d): %f\n", bill->name, bill->age, bill->salary);
    free(bill);
}

Questions

  1. What is the value of bill->salary after the call to tax()?
  2. The above program has a memory leak. Which variable should also be freed?
  3. Why is there a +1 in the malloc call on bill->name?
  4. Suppose we had set p equal to a newly malloc'ed person in tax. How would this affect bill?

Model 3: The Object-Oriented Paradigm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import java.util.Random;
 
public abstract class Vehicle {
    protected int gear;
    protected boolean parked;
    protected int speed;
    protected boolean locked;
  
    public Vehicle() {
       this.gear = 1;
       this.parked = true;
       this.speed = 0;
       this.locked = true;
    }
  
    public void drive(int speed) {
       if(this.parked == true) {
         this.parked = false;
       }
       this.speed = speed;
    }
  
    // return a boolean if the vehicle is parked
    public abstract boolean stop();
  
    public int getSpeed() {
       return this.speed;
    }
  
    public void unlock() {
       this.locked = false;
    }
  
    public void lock() {
       this.locked = true;
    }
}
 
public interface Home {
    public int cook();
  
    public void makeBed();
}
 
public class Car extends Vehicle {
     public Car() {
       super();
     }
      
     public void drive(int speed) {
       super.drive(speed);
      
       this.gear = this.speed / 10;
     }
      
     // return a boolean if the vehicle is parked
     public boolean stop() {
       this.speed = 0;
       this.gear = 1;
       return this.parked;
    }
}
 
// Unlike interfaces, you can only extend a single class
public class MotorHome extends Vehicle implements Home {
     private boolean bedmade;
      
     public MotorHome() {
       super();
       this.bedmade = true;
     }
      
     public void drive(int speed) {
       super.drive(speed);
      
       this.gear = this.speed / 5;
     }
      
     // return a boolean if the vehicle is parked
     public boolean stop() {
       this.speed = 0;
       this.gear = 1;
       return this.parked;
     }
      
     public int cook() {
       return 1; // yum?
     }
      
     public void makeBed() {
       this.bedmade = true;
     }
      
     public boolean isBedMade() {
       return this.bedmade;
     }
}
 
class Main {
     public static void main(String[] args) {
       Random r = new Random();
      
       Vehicle[] vehicles = new Vehicle[2];
      
       Car c = new Car();
       MotorHome m = new MotorHome();
      
       vehicles[0] = c;
       vehicles[1] = m;
      
       m.makeBed();
      
       for(Vehicle v: vehicles) {
         v.drive(r.nextInt(50));
       }
      
       // Methods in the Interface can be called on variables whose type is just the interface, no matter what it really is!
       System.out.println("How fast is the car driving? " + vehicles[0].getSpeed());
      
       System.out.println("How about the Motor Home? " + vehicles[1].getSpeed());
      
       // Methods specific to a class can always be called from the object.
       System.out.println("Is the Motor Home bed made? " + m.isBedMade());
     }
}

Questions

  1. How did each file change above by converting the Vehicle interface to a class?
  2. By converting the Vehicle interface to a class, what can we now define in a Vehicle that we could not define in the interface?
  3. You can implement an interface; what is the keyword to subclass a class?
  4. From context, what do you think the abstract keyword means in the Vehicle class?
  5. From context, what do you think the call to super() does?
  6. Notice that some elements are scoped to be public and private, like before, but now some items are protected. Which items are protected, and in which files do they reside? What do you think this scope allows/disallows?
  7. Sketch out an object-oriented design representing students and courses, in which students are enrolled in one or more sections of the course.

Model 4: Imperative Languages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <math.h>
#include <stdio.h>
 
void calcroot(float* root, float discriminant, int b) {
    *root = -b + discriminant;
}
 
int main(void) {
    int a = 1, b = 2, c = 1;
     
    float discriminant = sqrt(4.0*a*c) / 2*a;
    float root = 0;
     
    calcroot(&root, discriminant, b);
    printf("%d\n", root);
     
    calcroot(&root, -discriminant, b);
    printf("%d\n", root);
}

Questions

  1. At what points do the value of root change? Where are they changed, and where are they effective?
  2. What would happen if a is 0?
  3. How many copies of discriminant exist on the call to calcroot?
  4. How does the calcroot function know to use the discriminant variable in its local function?
  5. What would happen if discriminant was modified inside calcroot? Why is this different than for the root variable?

Model 5: Declarative Languages - Logic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
course(CS173).
course(CS174).
course(CS374).
course(CS475).
 
prereq(CS173, CS174).
prereq(CS174, CS374).
 
?-prereq(CS475, CS374). % no
 
% Horn Clause: does there exist a Z such that Y is a prereq of Z AND that X must be taken before Z?
take_before(X, Y) :- prereq(Y, Z), take_before(X, Z). % Transitive closure through recursion
 
?-take_before(CS173, CS374). % yes

1
2
3
4
5
6
7
8
9
10
11
12
13
parent(alice, john).
parent(alice, mary).
parent(bob, john).
parent(bob, mary).
parent(susan, tom).
parent(susan, jane).
parent(david, tom).
parent(david, jane).
 
sibling(X, Y) :-
    parent(P, X),
    parent(P, Y),
    X \= Y.

Questions

  1. What query would result in a yes response according to the prerequisite rules above? How about a no?
  2. How could you check that two people are half-siblings such that one parent is in common, but the second parent is not the same? Hint: add more clauses to sibling to check that a third and fourth person is the parent of X and Y, respectively, and that those two parents are not equal.
  3. In your own words, what does the take_before clause specify, and how does it do so?
  4. Write a Horn Clause that specifies that one might go outside if it is warm and not raining outside.

Model 6: Declarative Languages - Prolog

1
2
3
4
5
perfect(N) :-
   between(1, inf, N), U is N // 2,
   findall(D, (between(1,U,D), N mod D =:= 0), Ds),
   sumlist(Ds, N).

Questions

  1. Describe, in your own words, what this program does.

Model 7: Applications of the Declarative Paradigm - Pythonic List Comprehension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
heights = [50, 62, 73, 58, 46, 49, 43]
minheight = 48
canride = []
 
# version 1
for height in heights:
    if height > minheight:
        canride.append(height)
   
# version 2
canride2 = [height for height in heights if height > minheight] 
 
# version 3
canridebool = list(map(lambda height: height > minheight, heights))
canride3 = [x[0] for x in zip(heights, canridebool) if x[1]]

Questions

  1. What are the advantages and disadvantages of each approach?
  2. Which version do you find more convenient and why?

Model 8: Scripting Languages - Bash

1
2
3
4
5
6
7
#!/bin/bash
 
COURSE=CS374
cd ${COURSE}
 
FILES=$(ls *.txt)
echo ${FILES}

Questions

  1. What is a statement in this language?
  2. How are variables expanded?
  3. What potential benefit can you see with the use of shell scripting?
  4. Are scripting languages imperative or declarative?

Model 9: Programming Constructs in bash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
 
let "x = $1 * 2" # equivalent to x=$(expr $1 \* 2) and x=$(($1 * 2))
let x++
 
NAME="Alexandra"
echo ${NAME} has ${#NAME} characters in it
echo ${NAME} goes by ${NAME:0:4} for short.
echo ${NAME%a*} ${NAME%%a*} # the # sign instead of % in the variable expansion
                            # does this behavior from the left instead of the right   
 
echo ${NAME/xandra/jandro}
                             
echo Here is a name that doesn't exist yet: ${NAME2}
echo Here is the value of another variable: ${NAME2:=Unknown}
echo How about now: ${NAME2}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
 
x=0
while [[ ${x} -lt 10 ]] # (( ${x} < 10 )) is also acceptable
do
        echo ${x}
        x=$((${x} + 1))
done
 
if [[ ${x} -eq 10 ]] # (( ${x} == 10 )) is also acceptable
then
   echo "x is now 10"
else
   echo "x is not 10 but rather ${x}"
fi
 
if [[ -e 'doit.sh' ]]
then
   echo "doit.sh exists"
else
   echo "doit.sh does not exist"
fi
 
if [[ -d 'doit.sh' ]]
then
   echo "doit.sh is a directory"
else
   echo "doit.sh is not a directory"
fi        

Questions

  1. What do you think $1 means?
  2. If $1 is 5, what is the final value of x?
  3. Why was the \ character necessary in the x=$(expr $1 \* 2) command?
  4. What do each of the variable expansions do in the first example above?
  5. What would happen if the -lt in the loop above was modified to <? Similarly, what is the difference between -eq and = in bash?

Model 10: Concurrent and Socket Programming in the Go Language

Questions

  1. How might you add concurrency to a socket program to allow the program to send data without having to wait to receive a message?

Submission

I encourage you to submit your answers to the questions (and ask your own questions!) using the Class Activity Questions discussion board. You may also respond to questions or comments made by others, or ask follow-up questions there. Answer any reflective prompt questions in the Reflective Journal section of your OneNote Classroom personal section. You can find the link to the class notebook on the syllabus.