added computer programming notes

master
Stefan 7 months ago
parent 2238a8da3f
commit c6a0441121

@ -0,0 +1,122 @@
# Computer Programming Introduction
## Academic fraud - system detection
#### Don't do:
* Plagiarism
* Sharing (parts of) your homework
* ***Publishing*** (parts of) assignments, exams, answers
* Using LLMs to solve assignments
#### Do:
* Cite sources
* Always check what is **allowed by the course**
* Consult student advisor in case of time pressure
Plagiarism detection tools can detect **obsfucated** code based off of structure.
## Course elements
1. [Canvas](https://canvas.vu.nl/courses/72329)
2. Programming labs
3. zyBook
4. Classroom sessions
5. Exam
## Passing
### Programming Lab Grade
* Weighted average of 6 homeworg assignments (later weeks count more)
* Homework grades go up to **8.5**
* For a higher lab grade: **do challange assignments**
* Warm-up and code review assignments to be done by deadline
### Pass the course
* **Exam grade** >= 6
* Overall grade: weighted average (lab assg. 35%, exam 65%)
* Overall grade must be >= **5.5**
* Completing zyBook results in overall grade + 1.0 (only if **passsed already**; **deadline**: exam day)
### More info
* Passing the exam is a must
* Passing with a lab grade below 5.5 is possible
* No deadline extensions for assignments
* **Resit**: only for the exam
## Modules
1 module per week
<br>
Do the **zyBook** before class
1. Data Types, Variables, Control Flow (start: *6 sept 2023*)
<br> **Weight**: 10
2. Vectors, Streams, Functions
<br> **Weight**: 10
3. Error Handling, Testing
<br> **Weight**: 10
4. Recursion
<br> **Weight**: 20
5. Classes and Objects
<br> **Weight**: 25
6. Pointers and memory management
<br> **Weight**: 25
## Assigments
#### Warmup assignments
* You are allowed to form a group with another student (same lab group)
* Explain solution to TA
* Acceptance: *Pass or Fail*
* **Fail** = improve + resubmit
* Assignment must be accepted by the deadline (*or one week later + penalty*)
#### Code review
* Understand someone else's program + give feedback
* TA grades the review
* Grading: *Pass or Fail*
* Assignment must be accepted by the deadline (*or one week later + penalty*)
#### Homework
Main grading assignment
* Exercise the module's topic
* Graded by a **randomly** assigned TA
* Detailed grading
* Feedback by peer review (code review)
* Must be submitted by the **deadline** (1 point penalty per day late; 3 days late
= fail)
<br>
**!!! No resubmission once graded**
#### Challange
* Only in weeks **2 - 6**
* **!!!** Both warm-up and review of the same week have to be accepted
* To be solved during a lab session
* Inform your TA ahead that you want to do it
* Begging of lab session, TA gives you the challenge
* 10 = solve in 90 min, 8.5 = solve in 24h
## Schedule
* Classroom sessions
<br>
3x current week, 2x coming weeks
* Programming labs
<br>
1x current week, 3x coming weeks
<br>
**important** for questions and assignments acceptance; prepare assignments at home
<br>
challange assignments
<br>
**HOWTO:** See canvas
* **! Exam**
<br>
23rd Octobor
## Good to know:
* Contact TAs via [Canvas](https://canvas.vu.nl/courses/72329).
* Lectures should not be used for knowledge transfer, instead use zyBook.
* You are allowed to **resubmit** code that you already wrote.
* Submit assigments through **CodeGrade** (available on Canvas)
* You are allowed to resubmit code after the automatic test fails
## Course content
* **Not** a C++ course
* Test programs for correct behaviour
* Analyse programs and correct programming mistakes

@ -0,0 +1,35 @@
# Module 1 - Introduction to C++
## Binary prorgrams
**Ways to write**:
* Programming bit by bit
* Write in human-friendy notation
## C++ the language
* Compiled language
* **GOOD TO KNOW:** Using **C++ 14**
## The computer, simplified model
`INPUT DEVICE` ->
<br>
{ <br>
* `CENTRAL PROCESSING UNIT`
* `CONTROL UNIT`
* `ARITHMETIC / LOGIC`
* `MEMORY UNIT`
<br>
}
<br>-> `OUTPUT DEVICE`
## Elements of a useful program
* Input data, output data
<br> If a program does not itneract with its environment it is plain **useless**
* Data stored in **memory**
* Computation that transforms data into results
## Types
* Types are a **safety** net
* Terrible example **JAVASCRIPT**
* <span style="color:red">!!! **Do not use** `auto`</span>

@ -0,0 +1,59 @@
# Module 1 - P2
# Constant expressions
* Do **not** use numerical constants (unless obvious: 0, 1)
* Declare values symbolically
```cpp
const double PI = 3.14159;
```
```cpp
// Examples:
PI = 7; // throws an error:
// assignment to constant
double c = 2 * PI * r; // OK: reading PI
```
* Using a constant `PI` allows us to change to precision at any point
* Names avoid *magic constants*:
```cpp
const int SPEED_OF_LIGHT = 299792458;
```
### Const variable assignment
```cpp
const int MAX_SIZE = 100;
void use(int n)
{
const int C1 = MAX_SIZE + 7;
// OK: C1 is 107
const int C2 = n + 7;
// OK: and C2 is now *immutable*
}
```
* It is good style to declare values as `const`
* Constant at runetime
# Computation and code structure
* Code organisation and a proper structure help keeping code maintainable
when it grows beyond trivial size.
* Avoid spaghetti code
## Structuring code
* **Abstraction**: black boxes hide details of their internal workings
* Example: using `sqrt()` achieving its job and not caring about how it works
* *Stacking levels of abstraction*
## Indentation
* Indentation is important to visualise program structure
* Should be used **consistently**
# Rand
* `rand()` internally starts from a seed number and computes from there
* Seeding rand()
```cpp
// Seeding the random generator
// Use once in main()
void srand(unsigned int seed)
```

@ -0,0 +1,83 @@
# Module 2
# Functions in math
In maths, a function is a relation between a set of inputs and a set of permissible outputs with the property that each input is related to exactly one output. An example is the function that relates each real number $x$ to its square $x^2$. The output of a function f corresponding to an input x is denoted by $f(x)$ (read _f of x_)
# Functions in C++
A function is defiend by its sets (types) of input parameters, its name, the set of the return value, and the algorithm that computes the return value from the input values.
```cpp
double square(double x)
{
return x * x;
}
// Use call:
double sq = square(5.3);
```
# What makes a good function
- A good function operates in analogy to the notion from mathematics:
- Only use the input parameters (and nothing else, _no global variables_)
- **return** the result
- Test: the function name should inidcate an activity, like _compute the square of x_
- A pure function (as in math) is **stateless**
- it reads only its inputs (and nothing else) and it only writes its return
value
- a stateless function has no memory (no state) across calls
- a stateless function can be called over and over again, the results will be
repoducibily the same, no matter if / how it got called before
- Sometimes, a function is still a good level of abstraction in a program, even
if it violates what is said above
# State
- Computing is all about state: data stored in variables in memory, and about modifying this state
- Problems arise when such state is freely accesible from all parts of a program
- This opens the door for lazy, unintended interaction betweem seemingly unrelated pieces of code
## Function call and return
- When a function is called, the _thread of execution_ continues with the code of the function
- As soon as the function returns, the code that had called the function continues
```cpp
int f(int x)
{
return 2 * x;
}
int main()
{
int x = 7;
int y = x + 2;
x = f(y);
std::cout << x;
return 0;
}
```
## Return from a function
- A function returns in two cases
1. It reaches a `return` statement
2. It reaches the end of its code block
- A `void` function (not returning a value) is allowed to reaech the end of its code block without returning anything
- A function that does not return a value must, in any case, reach a `return` statement (or throw an _exception_)
- Compilers cannot always analyse if this is true
# Vector
- A vector is a sequence of elements that can be accessed by their index
- A vector has a notion of size
- Is defined with the data type of its elements and with its size.
- `v[2000]` is not checked at runtime **_BUT_** `v.at(2000)` is and throws an error if the index doesn't exist in the given vector. **_CONCLUSION_** Use `at()`

@ -0,0 +1,235 @@
# Error handling
- Exceptions
- Assertions
# Run-Time errors
```cpp
/* ... other code ... */
int area(int length, int width) {
return length * width;
}
int framed_area(int x, int y) {
return area(x - 2, y - 2);
}
int main() {
int x = -1;
int y = 2;
int z = 4;
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z);
double ratio = static_cast<double>(area1) / area3;
return 0;
}
```
area3 = 0 => dividing by 0
### 1. The caller deals with errors
This causes lots of error handling code, making the program harder to understand (causing more errors).
### 2. The function deals with errors
Sometimes, this is impossible:
- When we cannot modify the function (e.g. in a library)
- The called function does not know what to do in case of an error
- The called function does not know where it was called from
- Performance (error handling code adds instruction to both code size and execution time)
### Error reporting
- We could also make a function return an _error value_
<br>Example: `main()` when it returns something **!= 0**
```cpp
int area(int length, int width) {
if (length <= 0 || width <= 0) return -1;
return length * width;
}
```
**Drawbacks**:
- Not always possible (often, no _error value_ exists)
- Now, both caller and function must check for errors
### So, what can we do?
- Check your arguments in a function unless you have a good reason not to.
- Throw **an exception** in case of bad arguments
# Exceptions
- Are supposed to make error handling _easier_ (\*not _easy_)
<br>**Testing bad arguments**:
```cpp
class BadArea {}; // trivial, user-defined type
// just a name
int area(int length, int width) {
if (length <= 0 || width <= 0) throw BadArea();
return length * width;
}
```
<br>**_Try_ Some code and _Catch_ an exception**:
```cpp
int main() {
try {
int x = -1, y = 2, z = 4;
int area1 = area(x, y), area2 = framed_area(1, z), area3 = framed_area(y, z);
double ratio = area1 / area3;
}
catch (BadArea) {
cout << "Error occured!\n";
}
return 0;
}
```
### Exceptions and control flow
- Throwing an exception disrupts the normal sequential control flow
- Code immediately returns after an error occured
### Range errors (with Vectors)
The vecotr class throws a special `out_of_range` exception:
```cpp
int main() {
try {
std::vector<int> v(5);
int x = v.at(5);
}
catch (std::our_of_range) {
std::cerr << "Oops! Range error\n";
return 1;
}
catch (/*...*/) {
std::cerr << "Unknown exception\n";
return 2;
}
return 0;
}
```
<br>**! _Useful note_**: use `std::cerr` when handling error messages.
### Exceptions in the standard library
- `out_of_range`
- `runtime_error`, with a `string` param
### Catching in `main()`
```cpp
int doSomething() {
throw std::runtime_error("shit happens!");
return 42;
}
int main() {
try {
int dummy = doSomething();
return 0;
}
catch (std::runtime_error& e) {
std::cerr << "runtime error: " << e.what() << std::endl;
return 1;
}
}
```
<br>**! _Useful note_**: Use `std::runtime_error& err` not `std::runtime_error err` in the `catch` block.
### Exceptions are identified by their class (Type)
```cpp
int main() {
try {
// our program code
return 0;
}
catch (std::out_of_range& e) {
cerr << "Oops1";
return 2;
}
catch (std::out_of_range7 e) {
cerr << "Oops2";
return 1;
}
catch (/*...*/) {
cerr << "OoopsN";
return 42;
}
}
```
<br>
# Assert
Your program makes certain assumptions under which it is going to work.
<br>**Examples**:
- The user does not enter incorrect data
- the parameter n for a function `fibonacci(n)` is larger than 0
- The vector given to `binarySearch(v)` is sorted
### Example of `assert()`
```cpp
#include <cassert>
int factorial(int n) {
assert(n >= 0);
int f = 1;
for (int i = 1; i <= n; i++) {
f *= i;
assert(f>0); // DEBUG: no integer overflow
}
assert(i == n + 1); // DEBUG: loop cond
return f;
}
```
### Assert pseudocode
`assert()` works usually like this:
```cpp
void assert(int expression) {
if (expression == 0) {
// print error message
// abort program
}
}
```
### Removing assert debugging
- Use cmake's `-DCMAKE_BUILD_TYPE=Release` option instead of _Debug_.
<br> By default, cmake turns off debug messages from `assert()` when put into release mode.
- Use `#define NDEBUG` at the begining of the code.
# Assert vs Exceptions vs Error handling
- **Assertions**: for debugging
- **Exceptions**: for exceptional circumstances
- **Error handling**: for conditions you must handle
<br>You expect a number from `std::cin` but the user types something else

@ -0,0 +1,124 @@
# Module 3 - Part 2
- File input and output, command line parameters
- Unit testing
- Scope
# Input & Output with files
- The stream abstraction can easily be extended to files
- To do so, open a file and close it (also test if **opening worked**)
### File input
```cpp
// new stuff
std::ifstream inFS;
inFS.open("file.txt");
bool opened = inFS.is_open();
inFS >> value1 >> value2;
inFS.close();
```
### File output
```cpp
// new stuff
std::ofstream outFS;
outFS.open("outputfile.txt");
bool opened = outFS.is_open();
outFS << "Hello, world\n";
outFS.close();
```
# Streams: hierarchial abstraction
- Input streams allow to read `>>` sequences of data (characters) into variables of different types -> `istream`
- Output streams do the same with output `<<` -> `ostream`
**Types**:
- std::cin | std::cout
- std::istringstream | std::ostringstream
- std::ifstream | std::ofstream
# Command line parameters
```cpp
#include <vector>
#include <iostream>
std::vector<std::string> argumentVector(int argc, char** argv) {
std::vector<std::string> result(argc);
for (int i = 0; i < argc; i++) {
result.at(i) = argv[i];
}
return result;
}
int main(int argc, char** argv) {
std::vector<std::string> arguments = arguments(argc, argv);
for(int i = 0; i < arguments.size(); i++) {
std::cout << arguments.at(i) << std::endl;
}
return 0;
}
```
# Unit testing
- **Good practice**: develop your code in small, independent **units** and test thhem separately before using them together
- This is called _unit testing_
- **Units**:
- functions
- classes (mod 5)
### Test harnesses are separate code
A test harness is a separate `main()` program, that verifies if our program's results are correct
# Scopes
A scope is a region of a program text
<br>A name is declared in a scope and is valid (_in scope_) fro the point of its declaration until the end of its scope
```cpp
void f() {
g(); // error: g() is not in scope
}
void g() {
f(); // OK: f() is in scope
}
void h() {
int x = y; // error: y is not yet in scope
int y = x; // OK
{
int z; // OK
}
int whatever = z; // error: z is out of scope
g(); // OK
}
```
### Scopes keep names local
```cpp
int x = 42;
void f(int x) {
int z = x + 7;
}
void g(int x) {
int f = x + 2;
return 2 * f;
}
```

@ -0,0 +1,136 @@
# Functions & Templates
# Function templates
Same functionality does not depend on the type of objects used:
```cpp
void swap(double &d1, double& d2) {
double temp = d1;
d1 = d2;
d2 = temp;
}
void swap(std::string& s1, std::string& s2) {
std::string temp = s1;
s1 = s2;
s2 = temp;
}
```
Implementing this functionality with `template`:
```cpp
template <typename T> void swap(T& t1, T& t2) {
T temp = t1;
t1 = t2;
t2 = temp;
}
int main() {
double d1 = 42.0, d2 = -7.5;
swap(d1, d2);
std::string s1 = "aaa", s2 = "bbb";
swap(s1, s2);
return 0;
}
```
<br>A template can have more than one type parameter: `template <typename T1, typename T2, ...>`
<br>**Example**:
- `std::vector<T>`
- `std::map<Key, Value>`
# Compiling templates
A template function or class is just a code template (blueprint), not _real_ code
<br>Template code will be created only when used
- Using `swap(double, double)` will cause the compiler to create an actual function: `void swap(double& d1, double& d2)`
<br>**Conclusion**: It is impossible to compile a template by its own.
### Solution
Put templates into a **header file** that will be included by the program.
```cpp
#include <iostream>
#include "swap.h"
int main() {
char c1 = 'a', c2 = 'X';
swap(c1, c2);
int i1 = 1, i2 = 2;
swap(i1, i2);
return 0;
}
```
# Function activation record
When `expression()` is called, the C++ implementation sets aside space for:
- parameters
- local variables
- _stuff_ for returning the result
<br>This information is called _implementation record_
# The stack data structure
A stack is a container where we can put something on top of it, and can only get the topmost element back
<br>Each time a function is called, its activation record is pushed onto the execution stack (known as **_the stack_**)
# Recurssion
`expression()` indirectly calls itself. We call this indirect call loop: **recursion**
- A function that calls itself is called _recursive_
- Keep the model of the stack activation records in mind
- Think of a recursive function as a function that calls another function (that happens to use the very same code)
## Recursive functions (as in math)
- Toy example: factorial numbers
$$5! = 5 \times 4 \times 3 \times 2 \times 1 = 120$$
$$n! = n \times (n-1)!$$
- Code example:
```cpp
long long factorial(long long n)
{
assert(n > 0);
if (n == 1) return 1;
return n * factorial(n - 1);
}
```
Unfortunately, `factorial()` uses tail recursion, which is a bad way of using recursion
## Fibonacci numbers recurisvely
```cpp
long long fibonacci(long long n)
{
assert(n >= 0);
switch (n)
{
case 1:
case 2:
return 1;
default:
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
```
Loading…
Cancel
Save