Rename year directories to allow natural ordering
@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int my_int;
|
||||
int* my_int_pointer;
|
||||
long my_long;
|
||||
double * my_double_pointer;
|
||||
char ** my_char_pointer_pointer;
|
||||
|
||||
printf("The size of my_int is %lu bytes\n", sizeof(my_int));
|
||||
printf("The size of my_int_pointer is %lu bytes\n", sizeof(my_int_pointer));
|
||||
printf("The size of my_long is %lu bytes\n", sizeof(my_long));
|
||||
printf("The size of my_double_pointer is %lu bytes\n", sizeof(my_double_pointer));
|
||||
printf("The size of my_char_pointer_pointer is %lu bytes\n", sizeof(my_char_pointer_pointer));
|
||||
}
|
8
year3/semester1/CT331: Programming Paradigms/assignments/assignment1/code/question2/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/question2.iml" filepath="$PROJECT_DIR$/.idea/question2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "linkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "linkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
char* data;
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size){
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
char* dataPointer = malloc(sizeof(char)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, char* data, size_t size){
|
||||
listElement* newEl = createEl(data, size);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size);
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, char* data, size_t size);
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size);
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, char* data, size_t size);
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
|
||||
#endif
|
@ -0,0 +1,57 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "linkedList.h"
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
|
||||
listElement* l = createEl("Test String (1).", sizeof("Test String (1)."));
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
printf("Testing insertAfter()\n");
|
||||
listElement* l2 = insertAfter(l, "another string (2)", sizeof("another test string(2)"));
|
||||
insertAfter(l2, "a final string (3)", sizeof("a final string(3)"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"));
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "genericLinkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "genericLinkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
void* data;
|
||||
void (*printFunction)(void*);
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(void* data, size_t size, void (*printFunction)(void*)) {
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
void* dataPointer = malloc(sizeof(void)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
|
||||
e->printFunction = printFunction;
|
||||
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
current->printFunction(current->data);
|
||||
// printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, void* data, size_t size, void (*printFunction)(void*)){
|
||||
listElement* newEl = createEl(data, size, printFunction);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, void* data, size_t size, void (*printFunction)(void*)) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size, printFunction);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, void* data, size_t size, void (*printFunction)(void*)) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size, printFunction);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
|
||||
#endif
|
@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "genericLinkedList.h"
|
||||
|
||||
// functions to print out different data types
|
||||
// a more professional design might be to put these in the genericLinkedList header file but i only need these for testing purposes
|
||||
void printChar(void* data) {
|
||||
printf("%c\n", *(char*) data);
|
||||
}
|
||||
|
||||
void printStr(void* data) {
|
||||
printf("%s\n", (char*) data);
|
||||
}
|
||||
|
||||
void printInt(void* data) {
|
||||
printf("%d\n", *(int*) data);
|
||||
}
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
|
||||
listElement* l = createEl("Test String (1).", sizeof("Test String (1)."), printStr);
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
printf("Testing insertAfter()\n");
|
||||
listElement* l2 = insertAfter(l, "another string (2)", sizeof("another string (2)"), printStr);
|
||||
insertAfter(l2, "a final string (3)", sizeof("a final string (3)"), printStr);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"), printStr);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"), printStr);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("Testing pushing different data types\n");
|
||||
int myint = 42;
|
||||
push(&l, &myint, sizeof(myint), printInt);
|
||||
char mychar = 'c';
|
||||
push(&l, &mychar, sizeof(mychar), printChar);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
@ -0,0 +1,343 @@
|
||||
%! TeX program = lualatex
|
||||
\documentclass[a4paper]{article}
|
||||
|
||||
% packages
|
||||
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
||||
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
||||
\usepackage[final, colorlinks = false, urlcolor = cyan]{hyperref}
|
||||
\usepackage{changepage} % adjust margins on the fly
|
||||
\usepackage{fontspec}
|
||||
|
||||
\usepackage{minted}
|
||||
\usepackage{xcolor}
|
||||
|
||||
\usepackage{pgfplots}
|
||||
\pgfplotsset{width=\textwidth,compat=1.9}
|
||||
|
||||
\usepackage{caption}
|
||||
\newenvironment{code}{\captionsetup{type=listing, skip=0pt}}{}
|
||||
% \captionsetup{skip=0pt}
|
||||
% \setlength{\abovecaptionskip}{3pt}
|
||||
% \setlength{\belowcaptionskip}{5pt}
|
||||
|
||||
\usepackage[yyyymmdd]{datetime}
|
||||
\renewcommand{\dateseparator}{--}
|
||||
\setmainfont{EB Garamond}
|
||||
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
||||
|
||||
\usepackage{titlesec}
|
||||
% \titleformat{\section}{\LARGE\bfseries}{}{}{}[\titlerule]
|
||||
% \titleformat{\subsection}{\Large\bfseries}{}{0em}{}
|
||||
% \titlespacing{\subsection}{0em}{-0.7em}{0em}
|
||||
%
|
||||
% \titleformat{\subsubsection}{\large\bfseries}{}{0em}{$\bullet$ }
|
||||
% \titlespacing{\subsubsection}{1em}{-0.7em}{0em}
|
||||
|
||||
% margins
|
||||
\addtolength{\hoffset}{-2.25cm}
|
||||
\addtolength{\textwidth}{4.5cm}
|
||||
\addtolength{\voffset}{-3.25cm}
|
||||
\addtolength{\textheight}{5cm}
|
||||
\setlength{\parskip}{0pt}
|
||||
\setlength{\parindent}{0in}
|
||||
% \setcounter{secnumdepth}{0}
|
||||
|
||||
\begin{document}
|
||||
\hrule \medskip
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedright
|
||||
\footnotesize
|
||||
Name: Andrew Hayes \\
|
||||
E-mail: \href{mailto://a.hayes18@universityofgalway.ie}{\texttt{a.hayes18@universityofgalway.ie}} \hfill\\
|
||||
ID: 21321503 \hfill
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.4\textwidth}
|
||||
\centering
|
||||
\vspace{0.4em}
|
||||
\Large
|
||||
\textbf{CT331} \\
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedleft
|
||||
\today
|
||||
\end{minipage}
|
||||
\medskip\hrule
|
||||
\begin{center}
|
||||
\normalsize
|
||||
Assignment 1: Procedural Programming with C
|
||||
\end{center}
|
||||
\hrule
|
||||
|
||||
\section{Question 1}
|
||||
\subsection{Part (A): Code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[texcl, mathescape, linenos, breaklines, frame=single]{C}{../code/question1/question1.c}
|
||||
\caption{\texttt{question1.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.8\textwidth]{./images/question1.png}
|
||||
\caption{Console Output of \texttt{question1.c}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Part (B): Comments}
|
||||
The amount of memory allocated to variables of different types in C is determined at compile-time, and is dependent on the architecture of
|
||||
the machine for which it is being compiled and the compiler used.
|
||||
\begin{itemize}
|
||||
\item On my machine, using GCC, an \verb|int| is allocated 4 bytes. This is the usual amount allocated on both 32-bit and 64-bit systems (my machine being of the
|
||||
latter kind), although older 32-bit systems used 2 bytes for an \verb|int| (the same amount as for a \verb|short|).
|
||||
4 bytes is used even on 64-bit machines to maintain backwards compatibility with older 32-bit architectures.
|
||||
\item An \verb|int*| (a pointer to a variable of type \verb|int|) is allocated 8 bytes on my machine.
|
||||
This is because that my machine has a 64-bit architecture, and therefore an address in memory is represented using 64 bits (8 bytes).
|
||||
If this were compiled for a 32-bit machine, the size of an pointer would be 4 bytes since addresses are 32-bit.
|
||||
\item A \verb|long| is allocated 8 bytes on my machine. This is because my machine is 64-bit and a \verb|long| is typically 8 bytes in length on such machines.
|
||||
On 32-bit machines, a \verb|long| is typically 4 bytes.
|
||||
\item The size of a pointer to a \verb|double| is the same as the size of any other pointer on the same machine; on 64-bit machines, pointers are 8 bytes, and on
|
||||
32-bit machines, they are 4 bytes.
|
||||
The type of data to which a pointer points has no effect on the size of the pointer, as the pointer is just a memory address.
|
||||
\item A pointer to a \verb|char| pointer is the same size as any other pointer: 8 bytes on a 64-bit machine and 4 bytes on a 32-bit machine.
|
||||
Note: it might be more intuitive to refer to a ``character pointer pointer'' as a pointer to a string in certain situations, as strings are character arrays,
|
||||
and an array variable acts as a pointer to the first element in the array.
|
||||
\end{itemize}
|
||||
|
||||
\section{Question 2}
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size);
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, char* data, size_t size);
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{linkedList.h}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{linkedList.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"));
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{tests.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/question2.png}
|
||||
\caption{Console Output for Question 2}
|
||||
\end{figure}
|
||||
|
||||
\section{Question 3}
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/genericLinkedList.h}
|
||||
\caption{\texttt{genericLinkedList.h}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/genericLinkedList.c}
|
||||
\caption{\texttt{genericLinkedList.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/tests.c}
|
||||
\caption{\texttt{tests.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/question3.png}
|
||||
\caption{Console Output for Question 3}
|
||||
\end{figure}
|
||||
|
||||
\section{Question 4}
|
||||
\subsection{Part 1}
|
||||
Any algorithm for traversing a singly linked list in reverse will always first require traversing the list forwards, and will therefore be \emph{at least} somewhat
|
||||
less efficient than a forwards traversal.
|
||||
One of the simplest ways to traverse a linked list in reverse is to use a recursive function.
|
||||
\begin{code}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{C}
|
||||
void reverse_traverse(listElement* current){
|
||||
if (current == NULL) { return; }
|
||||
reverse_traverse(current->next);
|
||||
current->printFunction(current->data);
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Recursive Function to Traverse a Singly Linked List in Reverse}
|
||||
\end{code}
|
||||
|
||||
This is quite inefficient as it requires that the function call for each node persists on the stack until the last node is reached, using a lot of stack memory.
|
||||
Another approach would be to iteratively reverse the linked list, by making some kind of data structure, linked list or otherwise, that contains the data of the
|
||||
original linked list but in reverse, and then iterating over that forwards.
|
||||
This would likely be more efficient in terms of memory \& computation.
|
||||
\\\\
|
||||
Because traversing a linked list in reverse always requires traversing it forwards first, any reverse algorithm will take at least twice as much memory \& computation
|
||||
as traversing it forwards, which is $O(n)$.
|
||||
It will also require that some way of storing the data in reverse in memory, either explicitly with a data, like in the iterative approach, or in the same manner
|
||||
as the recursive approach, wherein the data is stored in reverse by the nested structure of the function calls: as each function call returns, the call structure
|
||||
is iterated through in reverse.
|
||||
Therefore, we also have at least $O(n)$ memory usage, as we have to store some sort of reverse data structure.
|
||||
|
||||
\subsection{Part 2}
|
||||
The simplest way in which the structure of a linked list could be changed to make backwards traversal less intensive is to change it from a singly linked list to a
|
||||
doubly linked list, i.e. instead of each node in the list containing a pointer to just the next node, make each node contain a pointer to both the next node \& the
|
||||
previous node.
|
||||
The backwards traversal of a doubly linked list is no more intensive than the forwards traversal of a linked list.
|
||||
The drawback of using a doubly linked list is that it requires slightly more memory per node than a singly linked list, as you're storing an additional pointer
|
||||
for every node.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
\end{document}
|
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 75 KiB |
@ -0,0 +1,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int arg, char* argc[]){
|
||||
printf("Hello assignment1.\n");
|
||||
|
||||
int my_integer;
|
||||
int* my_integer_pointer;
|
||||
long my_long;
|
||||
double *my_double_pointer;
|
||||
char **my_char_pointer_pointer;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "linkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int arg, char* argc[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "linkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
char* data;
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size){
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
char* dataPointer = malloc(sizeof(char)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, char* data, size_t size){
|
||||
listElement* newEl = createEl(data, size);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size);
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, char* data, size_t size);
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "linkedList.h"
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
listElement* l = createEl("Test String (1).", 30);
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
listElement* l2 = insertAfter(l, "another string (2)", 30);
|
||||
insertAfter(l2, "a final string (3)", 30);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// Test delete after
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
@ -0,0 +1,22 @@
|
||||
#lang racket
|
||||
|
||||
;; a cons pair of two numbers
|
||||
(cons 1 2)
|
||||
|
||||
;; a list of 3 numbers using only the cons function
|
||||
;; this could be more easily done using the single quote `'` (i.e., `'(1 2 3)`) but i don't use it as it seemed against the spirit of the question
|
||||
(cons 1 (cons 2 (cons 3 empty)))
|
||||
|
||||
;; a list containing a string, a number, and a nested list of three numbers using only the cons function
|
||||
(cons "a string"
|
||||
(cons 0
|
||||
(cons (cons 1 (cons 2 (cons 3 empty))) empty)
|
||||
)
|
||||
)
|
||||
|
||||
;; a list containing a string, a number, and a nested list of three numbers, using only the list function
|
||||
(list "a string" 0 (list 1 2 3))
|
||||
|
||||
;; a list containing a string, a number, and a nested list of three numbers, using only the append function
|
||||
;; using `'` as the arguments of the `append` function must be themselves lists
|
||||
(append '("a string") '(0) '((1 2 3)))
|
@ -0,0 +1,93 @@
|
||||
#lang racket
|
||||
|
||||
(provide ins_beg)
|
||||
(provide ins_end)
|
||||
(provide count_top_level)
|
||||
(provide count_instances)
|
||||
(provide count_instances_tr)
|
||||
(provide count_instances_deep)
|
||||
|
||||
;; function to insert an element at the beginning of a list
|
||||
(define (ins_beg el lst)
|
||||
;; assuming that the second element is always a list
|
||||
(cons el lst)
|
||||
)
|
||||
|
||||
;; function to insert an element at the end of a list
|
||||
(define (ins_end el lst)
|
||||
;; making el into a list if it isn't already
|
||||
(append lst (cons el empty))
|
||||
)
|
||||
|
||||
;; function to count the number of top-level items in a list
|
||||
(define (count_top_level lst)
|
||||
(if (null? lst)
|
||||
0 ;; return 0 if we've reached the end of the list
|
||||
(+ 1 (count_top_level (cdr lst))) ;; return 1 plus the count_top_level of the second element of the cons pair (the rest of the list)
|
||||
)
|
||||
)
|
||||
|
||||
;; non-tail recursive function to count the number of times a given item occurs in a list (assuming items are atomic)
|
||||
(define (count_instances item lst)
|
||||
(if (null? lst)
|
||||
0 ;; return 0 if at the end of the list
|
||||
(+
|
||||
(if (equal? item (car lst))
|
||||
1 ;; if the item is equal to the first element of the list, add 1
|
||||
0 ;; if the item is not equal to the first element of the list, add 0
|
||||
)
|
||||
(count_instances item (cdr lst)) ;; recurse with the remainder of the list
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; helper function for count_instances_tr
|
||||
(define (count_instances_tr_helper item lst cnt)
|
||||
(cond
|
||||
;; return the count if the end of the list is reached (0 for empty list)
|
||||
((null? lst)
|
||||
cnt
|
||||
)
|
||||
;; if the first element of the list is equal to the item
|
||||
((eq? (car lst) item)
|
||||
;; recurse with the remainder of the list and an incremented count
|
||||
(count_instances_tr_helper item (cdr lst) (+ cnt 1))
|
||||
)
|
||||
;; if the first element of the list is not equal to the item
|
||||
(else
|
||||
;; recurse with the remainder of the list and an unchanged count
|
||||
(count_instances_tr_helper item (cdr lst) cnt)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; tail recursive function to count the number of times a given item occurs in a list (assuming items are atomic)
|
||||
(define (count_instances_tr item lst)
|
||||
;; calling helper function with the list and the count so far (0)
|
||||
(count_instances_tr_helper item lst 0)
|
||||
)
|
||||
|
||||
;; function to count the number of times an item occurs in a list and its sub-lists
|
||||
(define (count_instances_deep item lst)
|
||||
(cond
|
||||
;; return nothing if we've reached the end of the list
|
||||
((null? lst)
|
||||
0
|
||||
)
|
||||
|
||||
;; if the first item is a list, recurse through the first element and then the rest and return the sum of the two results
|
||||
((pair? (car lst))
|
||||
(+ (count_instances_deep item (car lst)) (count_instances_deep item (cdr lst)))
|
||||
)
|
||||
|
||||
;; if the first element is equal to the item, add 1 to the count and recurse with the rest of the list
|
||||
((eq? item (car lst)) ; If the first element is equal to the item, increment count
|
||||
(+ 1 (count_instances_deep item (cdr lst)))
|
||||
)
|
||||
|
||||
;; else if the first element is not equal to the item, recurse with the rest of the list
|
||||
(else
|
||||
(count_instances_deep item (cdr lst))
|
||||
)
|
||||
)
|
||||
)
|
@ -0,0 +1,145 @@
|
||||
#lang racket
|
||||
|
||||
;; function to display the contents of a binary search tree in sorted order
|
||||
(define (display_contents bst)
|
||||
(cond
|
||||
;; if the binary search tree is null, print an empty string (nothing)
|
||||
[(null? bst) (display "")]
|
||||
|
||||
;; if the binary search tree has nodes
|
||||
[else
|
||||
;; display the contents of the left sub-tree of the current node
|
||||
(display_contents (cadr bst))
|
||||
|
||||
;; display the current node
|
||||
(display (car bst))
|
||||
(newline)
|
||||
|
||||
;; display the contents of the right sub-tree of the current node
|
||||
(display_contents (caddr bst))
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
;; function to search a tree and tell whether a given item is presesnt in a given tree
|
||||
(define (search_tree item bst)
|
||||
(cond
|
||||
;; return false if we've reached the end of the tree without finding a match
|
||||
((null? bst) #f)
|
||||
|
||||
;; return true if the current node is equal to the item
|
||||
((equal? item (car bst)) #t)
|
||||
|
||||
;; else return whether the item was found in the left sub-tree or the right sub-tree
|
||||
(else
|
||||
(or
|
||||
(search_tree item (cadr bst)) ;; search left sub-tree
|
||||
(search_tree item (caddr bst)) ;; search right sub-tree
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; function to insert an item into a binary search tree
|
||||
(define (insert_item item bst)
|
||||
(cond
|
||||
;; if there are no nodes in the tree, create a new tree with the item as the root
|
||||
((null? bst)
|
||||
(list item '() '())
|
||||
)
|
||||
|
||||
;; if the item is less than the current node, insert it into the left-hand side of the tree
|
||||
((< item (car bst))
|
||||
;; create new bst with same root node, same right-hand side, but a left-hand side that has had the item inserted
|
||||
(list (car bst) (insert_item item (cadr bst)) (caddr bst))
|
||||
)
|
||||
|
||||
;; if the item is greater than the current node, insert it into the right-hand side of the tree
|
||||
((> item (car bst))
|
||||
;; create new bst with same root node, same left-hand side, but a right-hand side that has had the item inserted
|
||||
(list (car bst) (cadr bst) (insert_item item (caddr bst)))
|
||||
)
|
||||
|
||||
;; else the item is equal to the current node, so do nothing
|
||||
(else bst)
|
||||
)
|
||||
)
|
||||
|
||||
;; function to insert a list of items into a binary search tree
|
||||
(define (insert_list lst bst)
|
||||
(if (null? lst)
|
||||
;; if the list is null, just return the bst with no changes
|
||||
bst
|
||||
|
||||
;; otherwise, recurse with the remainder of the list and the binary tree produced by inserting the first item of the list into bst
|
||||
(insert_list (cdr lst) (insert_item (car lst) bst))
|
||||
)
|
||||
)
|
||||
|
||||
;; tree-sort function
|
||||
(define (tree_sort lst)
|
||||
;; inserting the list into a tree structure to sort it and then displaying the contents of that tree
|
||||
(display_contents (insert_list lst '()))
|
||||
)
|
||||
|
||||
;; function to insert an item into a binary search tree based off a sorting function
|
||||
;; the sorting function should return accept two items and arguments, and return true if they were passed in order, and false otherwise or if they are equal
|
||||
(define (insert_item_custom item bst sorter)
|
||||
(cond
|
||||
;; if there are no nodes in the tree, create a new tree with the item as the root
|
||||
((null? bst)
|
||||
(list item '() '())
|
||||
)
|
||||
|
||||
;; if the item is goes before the current node, insert it into the left-hand side of the tree
|
||||
((sorter item (car bst))
|
||||
;; create new bst with same root node, same right-hand side, but a left-hand side that has had the item inserted
|
||||
(list (car bst) (insert_item_custom item (cadr bst) sorter) (caddr bst))
|
||||
)
|
||||
|
||||
;; if the item goes after the current node, insert it into the right-hand side of the tree
|
||||
((sorter (car bst) item)
|
||||
;; create new bst with same root node, same left-hand side, but a right-hand side that has had the item inserted
|
||||
(list (car bst) (cadr bst) (insert_item_custom item (caddr bst) sorter))
|
||||
)
|
||||
|
||||
;; else the item is equal to the current node, so do nothing
|
||||
(else bst)
|
||||
)
|
||||
)
|
||||
|
||||
;; sorter function which states whether the two arguments were supplied in strictly ascending order (i.e., if item == item2, return false)
|
||||
(define (sort_ascending item1 item2)
|
||||
(if (< item1 item2)
|
||||
#t
|
||||
#f
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; sorter function which states whether the two arguments were supplied in strictly descending order (i.e., if item == item2, return false)
|
||||
(define (sort_descending item1 item2)
|
||||
(if (> item1 item2)
|
||||
#t
|
||||
#f
|
||||
)
|
||||
)
|
||||
|
||||
;; sorter function which states whether the two arguments were supplied in strictly ascending order based on the final digit (i.e., if item == item2, return false)
|
||||
(define (sort_ascending_last item1 item2)
|
||||
(if (< (modulo item1 10) (modulo item2 10))
|
||||
#t
|
||||
#f
|
||||
)
|
||||
)
|
||||
|
||||
;; function to insert a list of items into a binary search tree in the order determined by a sorting function
|
||||
(define (insert_list_custom lst bst sorter)
|
||||
(if (null? lst)
|
||||
;; if the list is null, just return the bst with no changes
|
||||
bst
|
||||
|
||||
;; otherwise, recurse with the remainder of the list and the binary tree produced by inserting the first item of the list into bst
|
||||
(insert_list_custom (cdr lst) (insert_item_custom (car lst) bst sorter) sorter)
|
||||
)
|
||||
)
|
@ -0,0 +1,142 @@
|
||||
%! TeX program = lualatex
|
||||
\documentclass[a4paper]{article}
|
||||
|
||||
% packages
|
||||
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
||||
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
||||
\usepackage[final, colorlinks = false, urlcolor = cyan]{hyperref}
|
||||
\usepackage{changepage} % adjust margins on the fly
|
||||
\usepackage{fontspec}
|
||||
|
||||
\usepackage{minted}
|
||||
\usemintedstyle{algol_nu}
|
||||
\usepackage{xcolor}
|
||||
|
||||
\usepackage{pgfplots}
|
||||
\pgfplotsset{width=\textwidth,compat=1.9}
|
||||
|
||||
\usepackage{caption}
|
||||
\newenvironment{code}{\captionsetup{type=listing}}{}
|
||||
\captionsetup[listing]{skip=0pt}
|
||||
\setlength{\abovecaptionskip}{5pt}
|
||||
\setlength{\belowcaptionskip}{5pt}
|
||||
|
||||
\usepackage[yyyymmdd]{datetime}
|
||||
\renewcommand{\dateseparator}{--}
|
||||
\setmainfont{EB Garamond}
|
||||
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
||||
|
||||
\usepackage{titlesec}
|
||||
% \titleformat{\section}{\LARGE\bfseries}{}{}{}[\titlerule]
|
||||
% \titleformat{\subsection}{\Large\bfseries}{}{0em}{}
|
||||
% \titlespacing{\subsection}{0em}{-0.7em}{0em}
|
||||
%
|
||||
% \titleformat{\subsubsection}{\large\bfseries}{}{0em}{$\bullet$ }
|
||||
% \titlespacing{\subsubsection}{1em}{-0.7em}{0em}
|
||||
|
||||
% margins
|
||||
\addtolength{\hoffset}{-2.25cm}
|
||||
\addtolength{\textwidth}{4.5cm}
|
||||
\addtolength{\voffset}{-3.25cm}
|
||||
\addtolength{\textheight}{5cm}
|
||||
\setlength{\parskip}{0pt}
|
||||
\setlength{\parindent}{0in}
|
||||
% \setcounter{secnumdepth}{0}
|
||||
|
||||
\begin{document}
|
||||
\hrule \medskip
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedright
|
||||
\footnotesize
|
||||
Name: Andrew Hayes \\
|
||||
E-mail: \href{mailto://a.hayes18@universityofgalway.ie}{\texttt{a.hayes18@universityofgalway.ie}} \hfill\\
|
||||
ID: 21321503 \hfill
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.4\textwidth}
|
||||
\centering
|
||||
\vspace{0.4em}
|
||||
\Large
|
||||
\textbf{CT331} \\
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedleft
|
||||
\today
|
||||
\end{minipage}
|
||||
\medskip\hrule
|
||||
\begin{center}
|
||||
\normalsize
|
||||
Assignment 2: Functional Programming with Scheme
|
||||
\end{center}
|
||||
\hrule
|
||||
|
||||
\section{Question 1}
|
||||
\subsection{Part (A): Code}
|
||||
\begin{code}
|
||||
\inputminted[texcl, mathescape, breaklines, frame=single]{racket}{../code/assignment_q1.rkt}
|
||||
\caption{\texttt{assignment\_q1.rkt}}
|
||||
\end{code}
|
||||
|
||||
\subsection{Part (B): Comments}
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=\textwidth]{./images/question1.png}
|
||||
\caption{Output of \texttt{assignment\_q1.rkt}}
|
||||
\end{figure}
|
||||
|
||||
Comments on each line of output:
|
||||
\begin{enumerate}
|
||||
\item The \mintinline{racket}{cons} function creates a \mintinline{racket}{cons} pair, which is not always a ``list''.
|
||||
A list is a \mintinline{racket}{cons} pair in which the second element is another itself another list or is \mintinline{racket}{empty}.
|
||||
When a \mintinline{racket}{cons} pair that is not a list is printed, its elements are delimited by a ``\verb|.|'', as can be seen from the first line of
|
||||
output.
|
||||
|
||||
\item The second section of code produces a list of three numbers using only the \mintinline{racket}{cons} function:
|
||||
first we create a one-element list with \mintinline{racket}{(cons 3 empty)}, then we create a two-element list by making a \mintinline{racket}{cons} pair
|
||||
of \mintinline{racket}{2} and the already-created one-element list, and finally we create a three-element list by making a \mintinline{racket}{cons} pair
|
||||
of \mintinline{racket}{1} and the two-element list.
|
||||
This could of course be achieved far more simply by just using \mintinline{racket}{(cons 1 '(2 3))} or even justs \mintinline{racket}{'(1 2 3)} but I
|
||||
felt that this would be against the spirit of the exercise.
|
||||
|
||||
\item To create a nested list using only \mintinline{racket}{cons} in the third section of code, we make the \mintinline{racket}{'(1 2 3)} as previously,
|
||||
\mintinline{racket}{cons} it with \mintinline{racket}{empty} to make a nested list, and then \mintinline{racket}{cons} it with \mintinline{racket}{0}, and
|
||||
\mintinline{racket}{cons} that with a string literal.
|
||||
|
||||
\item Like \mintinline{racket}{cons}, \mintinline{racket}{list} can take either atomics or lists as arguments.
|
||||
To create the list using only the \mintinline{racket}{list} function, we can simply make a list of \mintinline{racket}{(list 1 2 3)}, and then create a
|
||||
list consisting of \mintinline{racket}{"a string"}, \mintinline{racket}{0}, \& the aforementioned list.
|
||||
This is much simpler than using \mintinline{racket}{cons} because \mintinline{racket}{list} can take as many arguments as we want, while
|
||||
\mintinline{racket}{cons} can only take two arguments.
|
||||
|
||||
\item Although I opted not to make use of the ``\mintinline{racket}{'}'' operator to create lists for the previous exercises, I make use of it here as there is
|
||||
no other way to create a list using only \mintinline{racket}{append} and nothing else, as \mintinline{racket}{append} only accepts lists as arguments.
|
||||
We make a list consisting of only one element (\mintinline{racket}{"a string"}), another list consisting of only one element (\mintinline{racket}{0}),
|
||||
and finally a list consisting of three elements \mintinline{racket}{'(1 2 3)} and append them into one to create the desired list.
|
||||
\end{enumerate}
|
||||
|
||||
\section{Question 2}
|
||||
\begin{code}
|
||||
\inputminted[breaklines, frame=single]{racket}{../code/assignment_q2.rkt}
|
||||
\caption{\texttt{assignment\_q2.rkt}}
|
||||
\end{code}
|
||||
|
||||
\section{Question 3}
|
||||
\begin{code}
|
||||
\inputminted[breaklines, frame=single]{racket}{../code/assignment_q3.rkt}
|
||||
\caption{\texttt{assignment\_q3.rkt}}
|
||||
\end{code}
|
||||
|
||||
It is worth noting here that the function \mintinline{racket}{sort_ascending_last} operates in a manner that may be undesirable.
|
||||
The function determines whether the two numbers passed to it as arguments were passed in strictly ascending order based on the final
|
||||
digit, i.e. it returns \mintinline{racket}{#t} if the final digit of the first argument is less than the final digit of the second
|
||||
argument, and \mintinline{racket}{#f} otherwise.
|
||||
Because this function considers only the final digit of the numbers, it considers two numbers to be equal if they share a final digit,
|
||||
e.g. it considers the numbers \mintinline{racket}{99} \& \mintinline{racket}{9} to be the same.
|
||||
Therefore, if one attempts to insert those two values into the binary search tree using this function as the ``sorter'', only the
|
||||
former value will get inserted, as binary search trees do not allow duplicate values, and the \mintinline{racket}{sort_ascending_last} function
|
||||
considers those two values to be equal.
|
||||
However, I thought it would be incorrect for the function to consider the $n-1$\textsuperscript{th} digits in the case of the $n$\textsuperscript{th} digits being identical, as
|
||||
a) the assignment did not ask for that and b) that would really just be no different to sorting the numbers in ascending order.
|
||||
|
||||
|
||||
|
||||
\end{document}
|
After Width: | Height: | Size: 20 KiB |
@ -0,0 +1,26 @@
|
||||
takes(tom, ct331).
|
||||
takes(mary, ct331).
|
||||
takes(joe, ct331).
|
||||
takes(tom, ct345).
|
||||
takes(mary, ct345).
|
||||
instructs(bob, ct331).
|
||||
instructs(ann, ct345).
|
||||
|
||||
% 1. rule that returns true if a given instructor teaches a given student
|
||||
teaches(Instructor, Student) :- instructs(Instructor, Course), takes(Student, Course).
|
||||
|
||||
% 2. query that uses the `teaches` rule to show all students instructed by bob
|
||||
?- teaches(bob, Student).
|
||||
?- findall(Student, teaches(bob, Student), Students).
|
||||
|
||||
% 3. query that uses the `teaches` rule to show all instructors that instruct mary
|
||||
?- teaches(Instructor, mary).
|
||||
?- findall(Instructor, teaches(Instructor, mary), Instructors).
|
||||
|
||||
% 5. rule that returns true if two students take the same course
|
||||
takesSameCourse(Student1, Student2) :- takes(Student1, Course), takes(Student2, Course).
|
||||
|
||||
contains1(Element, [Element | Tail]).
|
||||
|
||||
|
||||
contains2(Sublist, [Head | Sublist]).
|
@ -0,0 +1,5 @@
|
||||
% base case: any element is not in an empty list
|
||||
isNotElementInList(_, []).
|
||||
|
||||
% return true if Element is not the Head of the list and it's not found recursively searching the rest of the list
|
||||
isNotElementInList(Element, [Head | Tail]) :- Element \= Head, isNotElementInList(Element, Tail).
|
@ -0,0 +1,16 @@
|
||||
% predicate to merge two lists
|
||||
% base case: if the first list is empty, just return the second
|
||||
mergeTwoLists([], List, List).
|
||||
|
||||
% recursive predicate to merge two lists
|
||||
% split the first list into head and tail, and recurse with its tail and the second list until the first list is empty (base case)
|
||||
% then merge the original head of the first list with the resulting tail
|
||||
mergeTwoLists([Head | Tail], List2, [Head | ResultTail]) :- mergeTwoLists(Tail, List2, ResultTail).
|
||||
|
||||
% predicate to merge 3 lists
|
||||
% base case: merging an empty list and two others is the same as merging two lists
|
||||
mergeLists([], List2, List3, Merged) :- mergeTwoLists(List2, List3, Merged).
|
||||
|
||||
% split the first list into head and tail, and recurse with its tail and the other two lists until the first list is empty (base case)
|
||||
mergeLists([Head1 | Tail1], List2, List3, [Head1 | MergedTail]) :- mergeLists(Tail1, List2, List3, MergedTail).
|
||||
|
@ -0,0 +1,8 @@
|
||||
% call the helper predicate with the list to be reversed and an empty Accumulator to build up
|
||||
reverseList(List, Reversed) :- reverseListHelper(List, [], Reversed).
|
||||
|
||||
% base case fact: when the list to reverse is empty, the accumulator is the reversed list
|
||||
reverseListHelper([], Accumulator, Accumulator).
|
||||
|
||||
% recurse with the tail after prepending the head to the accumulator
|
||||
reverseListHelper([Head | Tail], Accumulator, Reversed) :- reverseListHelper(Tail, [Head | Accumulator], Reversed).
|
@ -0,0 +1,8 @@
|
||||
% base fact: if the list is empty, the list to be returned is just the element
|
||||
insertInOrder(Element, [], [Element]).
|
||||
|
||||
% if the element to be inserted is <= the head of the list, insert it at the head of the list
|
||||
insertInOrder(Element, [Head | Tail], [Element, Head | Tail]) :- Element =< Head.
|
||||
|
||||
% if the element to be inserted is greater than the head of the list, recurse with the tail of the list until
|
||||
insertInOrder(Element, [Head | Tail], [Head | NewTail]) :- Element > Head, insertInOrder(Element, Tail, NewTail).
|
@ -0,0 +1,295 @@
|
||||
%! TeX program = lualatex
|
||||
\documentclass[a4paper]{article}
|
||||
|
||||
% packages
|
||||
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
||||
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
||||
\usepackage[final, colorlinks = false, urlcolor = cyan]{hyperref}
|
||||
\usepackage{changepage} % adjust margins on the fly
|
||||
\usepackage{fontspec}
|
||||
|
||||
\usepackage{minted}
|
||||
% \usemintedstyle{algol_nu}
|
||||
\usepackage{xcolor}
|
||||
|
||||
\usepackage{pgfplots}
|
||||
\pgfplotsset{width=\textwidth,compat=1.9}
|
||||
|
||||
\usepackage{caption}
|
||||
\newenvironment{code}{\captionsetup{type=listing}}{}
|
||||
\captionsetup[listing]{skip=0pt}
|
||||
\setlength{\abovecaptionskip}{5pt}
|
||||
\setlength{\belowcaptionskip}{5pt}
|
||||
|
||||
\usepackage[yyyymmdd]{datetime}
|
||||
\renewcommand{\dateseparator}{--}
|
||||
\setmainfont{EB Garamond}
|
||||
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
||||
|
||||
\usepackage{titlesec}
|
||||
% \titleformat{\section}{\LARGE\bfseries}{}{}{}[\titlerule]
|
||||
% \titleformat{\subsection}{\Large\bfseries}{}{0em}{}
|
||||
% \titlespacing{\subsection}{0em}{-0.7em}{0em}
|
||||
%
|
||||
% \titleformat{\subsubsection}{\large\bfseries}{}{0em}{$\bullet$ }
|
||||
% \titlespacing{\subsubsection}{1em}{-0.7em}{0em}
|
||||
|
||||
% margins
|
||||
\addtolength{\hoffset}{-2.25cm}
|
||||
\addtolength{\textwidth}{4.5cm}
|
||||
\addtolength{\voffset}{-3.25cm}
|
||||
\addtolength{\textheight}{5cm}
|
||||
\setlength{\parskip}{0pt}
|
||||
\setlength{\parindent}{0in}
|
||||
% \setcounter{secnumdepth}{0}
|
||||
|
||||
\begin{document}
|
||||
\hrule \medskip
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedright
|
||||
\footnotesize
|
||||
Name: Andrew Hayes \\
|
||||
E-mail: \href{mailto://a.hayes18@universityofgalway.ie}{\texttt{a.hayes18@universityofgalway.ie}} \hfill\\
|
||||
ID: 21321503 \hfill
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.4\textwidth}
|
||||
\centering
|
||||
\vspace{0.4em}
|
||||
\Large
|
||||
\textbf{CT331} \\
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedleft
|
||||
\today
|
||||
\end{minipage}
|
||||
\medskip\hrule
|
||||
\begin{center}
|
||||
\normalsize
|
||||
Assignment 3: Declarative Programming with Prolog
|
||||
\end{center}
|
||||
\hrule
|
||||
|
||||
\section{Question 1}
|
||||
\subsection{Rule that returns true if a given instructor teaches a given student}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
teaches(Instructor, Student) :- instructs(Instructor, Course), takes(Student, Course).
|
||||
\end{minted}
|
||||
|
||||
\subsection{Query that uses the \mintinline{prolog}{teaches} rule to show all students instructed by \mintinline{prolog}{bob}}
|
||||
For this, I wasn't sure if the desired answer was a query that returned a student instructed by \mintinline{prolog}{bob}, followed by
|
||||
a couple semi-colons to get every student instructed by \mintinline{prolog}{bob}, or if the desired answer was a single query that
|
||||
returned a list of students taught by \mintinline{prolog}{bob}, so I did both.
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- teaches(bob, Student).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1.2.png}
|
||||
\caption{Using the \mintinline{prolog}{teaches} rule to show all students instructed by \mintinline{prolog}{bob}}
|
||||
\end{figure}
|
||||
|
||||
Alternatively, this could be done using the \mintinline{prolog}{findall()} predicate:
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- findall(Student, teaches(bob, Student), Students).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1_2_findall.png}
|
||||
\caption{Using the \mintinline{prolog}{teaches} rule \& the \mintinline{prolog}{findall} predicate to show all students instructed by \mintinline{prolog}{bob}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Query that uses the \mintinline{prolog}{teaches} rule to show all instructors that instruct \mintinline{prolog}{mary}}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- teaches(Instructor, mary).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1_3.png}
|
||||
\caption{Using the \mintinline{prolog}{teaches} rule to show all instructors that instruct \mintinline{prolog}{mary}}
|
||||
\end{figure}
|
||||
|
||||
Alternatively, this could be done using the \mintinline{prolog}{findall()} predicate:
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- findall(Instructor, teaches(Instructor, mary), Instructors).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1_3_findall.png}
|
||||
\caption{Using the \mintinline{prolog}{teaches()} rule \& the \mintinline{prolog}{findall()} predicate to show all instructors that instruct \mintinline{prolog}{mary}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Result of query \mintinline{prolog}{teaches(ann,joe).}}
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1_4.png}
|
||||
\caption{Result of query \mintinline{prolog}{teaches(ann,joe).}}
|
||||
\end{figure}
|
||||
|
||||
The result of the query \mintinline{prolog}{teaches(ann,joe).} is \mintinline{prolog}{false.} because \mintinline{prolog}{ann}
|
||||
only instructs \mintinline{prolog}{ct345} and \mintinline{prolog}{joe} only takes \mintinline{prolog}{ct331}, and therefore
|
||||
\mintinline{prolog}{ann} does not teach \mintinline{prolog}{joe} because \mintinline{prolog}{ann} does not teach a course
|
||||
that \mintinline{prolog}{joe} takes.
|
||||
|
||||
\subsection{Rule that returns \mintinline{prolog}{true} if two students take the same course}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
takesSameCourse(Student1, Student2) :- takes(Student1, Course), takes(Student2, Course).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q1_5.png}
|
||||
\caption{Queries to test \mintinline{prolog}{takesSameCourse()}}
|
||||
\end{figure}
|
||||
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- takesSameCourse(tom,mary).
|
||||
?- takesSameCourse(joe,mary).
|
||||
?- takesSameCourse(joe,tom).
|
||||
?- takesSameCourse(bob, mary).
|
||||
\end{minted}
|
||||
|
||||
\section{Question 2}
|
||||
\subsection{Query that displays the head \& tail of a list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- [Head | Tail] = [1,2,3].
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q2_1.png}
|
||||
\caption{Query to display the head \& tail of the list \mintinline{prolog}{[1,2,3]}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Display the head of a list, the head of the tail of the list, \& the tail of the tail of the list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- [Head | [HeadOfTail | TailOfTail]] = [1,2,3,4,5].
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q2_2.png}
|
||||
\caption{Query to display the head of the list, the head of the tail of the list, \& the tail of the tail of the list \mintinline{prolog}{[1,2,3,4,5]}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Rule that returns \mintinline{prolog}{true} if a given element is the first element of a given list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
contains1(Element, [Element | Tail]).
|
||||
|
||||
?- contains1(1, [1,2,3,4]).
|
||||
?- contains1(3, [1,2,3,4]).
|
||||
?- contains1(1, [2,3,4]).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q2_3.png}
|
||||
\caption{\mintinline{prolog}{contains1()} testing}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Rule that returns \mintinline{prolog}{true} if a given list is the same as the tail of another given list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
contains2(Sublist, [Head | Sublist]).
|
||||
|
||||
?- contains2([2,3,4], [1,2,3,4]).
|
||||
?- contains2([2,3,4], [1,2,3,4,5]).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q2_4.png}
|
||||
\caption{\mintinline{prolog}{contains2()} testing}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Query to display the first element of a given list using \mintinline{prolog}{contains1()}}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
?- contains1(FirstElement, [1,2,3,4,5]).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q2_5.png}
|
||||
\caption{Query to display the first element of a given list using \mintinline{prolog}{contains1()}}
|
||||
\end{figure}
|
||||
|
||||
\section{Determine if a given element is not in a given list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
% base case: any element is not in an empty list
|
||||
isNotElementInList(_, []).
|
||||
|
||||
% return true if Element is not the Head of the list and it's not found recursively searching the rest of the list
|
||||
isNotElementInList(Element, [Head | Tail]) :- Element \= Head, isNotElementInList(Element, Tail).
|
||||
|
||||
% testing
|
||||
isNotElementInList(1, []).
|
||||
isNotElementInList(1, [1]).
|
||||
isNotElementInList(1, [2]).
|
||||
isNotElementInList(2, [1, 2, 3]).
|
||||
isNotElementInList(7, [1, 2, 9, 4, 5]).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q3.png}
|
||||
\caption{Testing \mintinline{prolog}{isNotElementInList()}}
|
||||
\end{figure}
|
||||
|
||||
\section{Facts \& rules to merge three lists}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
% predicate to merge two lists
|
||||
% base case: if the first list is empty, just return the second
|
||||
mergeTwoLists([], List, List).
|
||||
|
||||
% recursive predicate to merge two lists
|
||||
% split the first list into head and tail, and recurse with its tail and the second list until the first list is empty (base case)
|
||||
% then merge the original head of the first list with the resulting tail
|
||||
mergeTwoLists([Head | Tail], List2, [Head | ResultTail]) :- mergeTwoLists(Tail, List2, ResultTail).
|
||||
|
||||
% predicate to merge 3 lists
|
||||
% base case: merging an empty list and two others is the same as merging two lists
|
||||
mergeLists([], List2, List3, Merged) :- mergeTwoLists(List2, List3, Merged).
|
||||
|
||||
% split the first list into head and tail, and recurse with its tail and the other two lists until the first list is empty (base case)
|
||||
mergeLists([Head1 | Tail1], List2, List3, [Head1 | MergedTail]) :- mergeLists(Tail1, List2, List3, MergedTail).
|
||||
|
||||
?- mergeLists([7],[1,2,3],[6,7,8], X).
|
||||
?- mergeLists([2], [1], [0], X).
|
||||
?- mergeLists([1], [], [], X).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q4.png}
|
||||
\caption{Testing \mintinline{prolog}{mergeLists()}}
|
||||
\end{figure}
|
||||
|
||||
\section{Facts \& rules to reverse a given list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
% call the helper predicate with the list to be reversed and an empty Accumulator to build up
|
||||
reverseList(List, Reversed) :- reverseListHelper(List, [], Reversed).
|
||||
|
||||
% base case fact: when the list to reverse is empty, the accumulator is the reversed list
|
||||
reverseListHelper([], Accumulator, Accumulator).
|
||||
|
||||
% recurse with the tail after prepending the head to the accumulator
|
||||
reverseListHelper([Head | Tail], Accumulator, Reversed) :- reverseListHelper(Tail, [Head | Accumulator], Reversed).
|
||||
|
||||
?- reverseList([1,2,3], X).
|
||||
?- reverseList([1], X).
|
||||
?- reverseList([], X).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q5.png}
|
||||
\caption{Testing \mintinline{prolog}{reverseList()}}
|
||||
\end{figure}
|
||||
|
||||
\section{Facts \& rules to insert an element into its correct position in a given list}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{prolog}
|
||||
% base fact: if the list is empty, the list to be returned is just the element
|
||||
insertInOrder(Element, [], [Element]).
|
||||
|
||||
% if the element to be inserted is <= the head of the list, insert it at the head of the list
|
||||
insertInOrder(Element, [Head | Tail], [Element, Head | Tail]) :- Element =< Head.
|
||||
|
||||
% if the element to be inserted is greater than the head of the list, recurse with the tail of the list until
|
||||
insertInOrder(Element, [Head | Tail], [Head | NewTail]) :- Element > Head, insertInOrder(Element, Tail, NewTail).
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
\includegraphics[width=\textwidth]{./images/q6.png}
|
||||
\caption{Testing \mintinline{prolog}{insertInOrder()}}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\end{document}
|
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 14 KiB |
@ -0,0 +1,608 @@
|
||||
%! TeX program = lualatex
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
% packages
|
||||
\usepackage{fontspec}
|
||||
\setmainfont{EB Garamond}
|
||||
% for tironian et fallback
|
||||
% % \directlua{luaotfload.add_fallback
|
||||
% % ("emojifallback",
|
||||
% % {"Noto Serif:mode=harf"}
|
||||
% % )}
|
||||
% % \setmainfont{EB Garamond}[RawFeature={fallback=emojifallback}]
|
||||
|
||||
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
||||
\usepackage[a4paper,left=2cm,right=2cm,top=\dimexpr15mm+1.5\baselineskip,bottom=2cm]{geometry}
|
||||
\setlength{\parindent}{0pt}
|
||||
|
||||
\usepackage{fancyhdr} % Headers and footers
|
||||
\fancyhead[R]{\normalfont \leftmark}
|
||||
\fancyhead[L]{}
|
||||
\pagestyle{fancy}
|
||||
|
||||
\usepackage{multicol}
|
||||
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
||||
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
||||
\usepackage[final, colorlinks = true, urlcolor = blue, linkcolor = black]{hyperref}
|
||||
\usepackage{changepage} % adjust margins on the fly
|
||||
|
||||
\usepackage{minted}
|
||||
\usemintedstyle{algol_nu}
|
||||
\usepackage{xcolor}
|
||||
|
||||
\usepackage{pgfplots}
|
||||
\pgfplotsset{width=\textwidth,compat=1.9}
|
||||
|
||||
\usepackage{caption}
|
||||
\newenvironment{code}{\captionsetup{type=listing}}{}
|
||||
|
||||
\usepackage[yyyymmdd]{datetime}
|
||||
\renewcommand{\dateseparator}{-}
|
||||
|
||||
\usepackage{titlesec}
|
||||
|
||||
\begin{document}
|
||||
\begin{titlepage}
|
||||
\begin{center}
|
||||
\hrule
|
||||
\vspace*{0.6cm}
|
||||
\huge \textbf{CT331}
|
||||
\vspace*{0.6cm}
|
||||
\hrule
|
||||
\LARGE
|
||||
\vspace{0.5cm}
|
||||
PROGRAMMING PARADIGMS
|
||||
\vspace{0.5cm}
|
||||
\hrule
|
||||
|
||||
\vfill
|
||||
\includegraphics[width=0.8\textwidth]{images/lambda.png}
|
||||
\vfill
|
||||
|
||||
\Large
|
||||
\vspace{0.5cm}
|
||||
\hrule
|
||||
\vspace{0.5cm}
|
||||
\textbf{Andreas Ó hAoḋa}
|
||||
% \vspace{0.5cm}
|
||||
% \hrule
|
||||
% \vspace{0.5cm}
|
||||
|
||||
\normalsize
|
||||
University of Galway
|
||||
|
||||
\today
|
||||
|
||||
\vspace{0.5cm}
|
||||
\hrule
|
||||
\end{center}
|
||||
\end{titlepage}
|
||||
|
||||
\pagenumbering{roman}
|
||||
\newpage
|
||||
\tableofcontents
|
||||
\newpage
|
||||
\setcounter{page}{1}
|
||||
\pagenumbering{arabic}
|
||||
|
||||
\section{Introduction}
|
||||
\subsection{Lecturer Contact Information}
|
||||
\begin{itemize}
|
||||
\item Finlay Smith, School of Computer Science.
|
||||
\item \href{mailto://finlay.smith@universityofgalway.ie}{\texttt{finlay.smith@universityofgalway.ie}}
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Syllabus}
|
||||
This module introduces three different programming paradigms: Procedural, Functional \& Logical.
|
||||
This will involve 3 programming languages: C (mostly function pointers - knowledge of C is assumed), LISP
|
||||
(a functional language) and Prolog (a logical language).
|
||||
Both LISP and Prolog will both be introduced but neither will be fully covered in this module.
|
||||
There are no books required or recommended for this course.
|
||||
|
||||
\subsubsection{Marking}
|
||||
30\% of the marks for this module will be for the three assignments (one for each paradigm).
|
||||
The remaining 70\% of the marks will be for the written exam.
|
||||
|
||||
\subsection{Programming Paradigms}
|
||||
A \textbf{paradigm} is a typical example or pattern of something; a pattern or model.
|
||||
A \textbf{programming paradigm} is a pattern or model of programming.
|
||||
Various types of programming languages are better suited to solving particular problems.
|
||||
Programming language implementations differ on semantics \& syntax:
|
||||
\begin{itemize}
|
||||
\item \textbf{Syntax} refers to the rules of the language; it allows us to form valid expressions \& statements.
|
||||
\item \textbf{Semantics} refers to the meaning of those expressions \& statements.
|
||||
\end{itemize}
|
||||
|
||||
Programming languages can be classified according to the features that they have with respect to both the conceptual \& implementation
|
||||
level.
|
||||
An alternative definition for a \textbf{programming paradigm} is a collection of abstract features that categorise a group of
|
||||
languages.
|
||||
|
||||
``\textit{The popularity of a paradigm is due to one community deciding which problems are important to solve and then supporting the
|
||||
most promising paradigm for attacking these problems.}'' -- Thomas Kuhn.
|
||||
|
||||
\subsection{Influences on Paradigms}
|
||||
\begin{itemize}
|
||||
\item Computer Capabilities.
|
||||
\item Applications.
|
||||
\item Programming Methods: Language designs have evolved to reflect changing understanding of good methods for writing
|
||||
large \& complex programs.
|
||||
\item Implementation Methods: Early compilers to optimised compilers; structured engineering to software engineering; data
|
||||
abstraction to OO.
|
||||
\item Theoretical Studies: Formal maths methods have deepened our understanding of strengths \& weaknesses of language
|
||||
features and thus influenced the choice \& inclusion of those features.
|
||||
\item Standardisation (has proved to be a strong conservative influence on the evolution of programming language design).
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Why Learn Different Paradigms?}
|
||||
\begin{itemize}
|
||||
\item Different paradigms make different trade-offs; What's tricky in one paradigm is ``baked in'' in another.
|
||||
\item Changing paradigms forces you to ``change gears''.
|
||||
\item It will prepare you for learning languages that you've never heard of or that may not exist yet.
|
||||
\item Helps you to decide what the best tool for the job is.
|
||||
\item Helps you to understand languages at a deeper level.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Why Learn Functional Programming?}
|
||||
\begin{itemize}
|
||||
\item It's one of the oldest paradigm (Lisp: 1958, still widely used today).
|
||||
\item Heavily based on mathematical concepts (proofs, lambda calculations).
|
||||
\item Elegant solutions (recursion).
|
||||
\item Other paradigms can be interpreted in terms of functional programming.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Why Learn Logical Programming?}
|
||||
\begin{itemize}
|
||||
\item Long history.
|
||||
\item ALlows implementation of things that are difficult in other paradigms.
|
||||
\item \emph{Very} different
|
||||
\item Helps to conceptualise logical problems.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Why Learn Imperative Programming?}
|
||||
\begin{itemize}
|
||||
\item The oldest paradigm -- goes back as far as punch cards \& magnetic loops.
|
||||
\item Much closer representation of how the machine actually works, i.e. ``closer to the metal''.
|
||||
\item Can help to recognise optimisation issues / computational bottlenecks.
|
||||
\item Contextualises many other systems (UNIX, Linux, etc.).
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Why Learn Object-Oriented Programming?}
|
||||
\begin{itemize}
|
||||
\item Tries to represent the real world.
|
||||
\item Abstraction \& inheritance.
|
||||
\item Object-Oriented is everywhere.
|
||||
\end{itemize}
|
||||
|
||||
\section{Overview of Object-Oriented Programming}
|
||||
Object-Oriented languages include:
|
||||
\begin{multicols}{2}
|
||||
\begin{itemize}
|
||||
\item Java.
|
||||
\item C\#.
|
||||
\item VB.NET.
|
||||
\item Scala.
|
||||
\item JavaScript.
|
||||
\item Python.
|
||||
\item PHP.
|
||||
\item Smalltalk.
|
||||
\item Ruby.
|
||||
\end{itemize}
|
||||
\end{multicols}
|
||||
|
||||
\subsection{Fundamentals of Object-Oriented Programming}
|
||||
\begin{itemize}
|
||||
\item Everything is an object.
|
||||
\item Computation is performed by message-passing.
|
||||
\item Every \textbf{object} is an \textbf{instance} of a \textbf{class} which is a grouping of similar objects.
|
||||
\item \textbf{Inheritance} describes the relationships between classes.
|
||||
\end{itemize}
|
||||
|
||||
Object-Oriented Programming focuses on the objects that the program represents and allows them to exhibit ``behaviour''.
|
||||
|
||||
\subsection{Four Major Principles of OOP}
|
||||
\subsubsection{Encapsulation}
|
||||
Data is hidden as if \textbf{encapsulated} within the object.
|
||||
Direct access to the data is restricted, instead we use methods to get, set, \& manipulate data.
|
||||
Manipulation of data is hidden.
|
||||
The object caller doesn't need to know what's actually going on behind the scenes.
|
||||
We can be (fairly) sure that nobody else is fiddling with our data.
|
||||
|
||||
\subsubsection{Abstraction}
|
||||
Functionality can be defined without actually being implemented.
|
||||
High-level interfaces provide method types/names without implementation.
|
||||
This allows case-specific implementation, allows one person to define the functionality \& another to implement, and
|
||||
allows the representation to be changed without affecting ``public'' view of the class.
|
||||
This is helpful when designing large systems.
|
||||
|
||||
\subsubsection{Inheritance}
|
||||
Classes can \textbf{inherit} functionality without re-implementing.
|
||||
This prevents the duplication of code.
|
||||
This is also helpful when designing large systems; it encourages a well-structured codebase.
|
||||
|
||||
\subsubsection{Polymorphism}
|
||||
Objects of one class can be treated like objects of other classes.
|
||||
|
||||
\section{Imperative \& Procedural Programming}
|
||||
\subsection{Imperative Programming}
|
||||
\textbf{Imperative programming} involves telling the computer to perform a set of actions, one after the other.
|
||||
Most programming languages have imperative aspects.
|
||||
Imperative programming consists of a list of instructions, \verb|GOTO| statements, and little or no structure.
|
||||
E.g., Assembly.
|
||||
|
||||
\subsection{Procedural Progamming}
|
||||
\textbf{Procedural progamming} splits actions into \textbf{procedures} or tasks.
|
||||
Procedures can be made up of other procedures (composition, recursion).
|
||||
The code is structured, uses ``functions'' or procedures, encourages code re-use, and encourages encapsulation \&
|
||||
composition.
|
||||
Note that procedural functions are not to be confused with Functional Programming.
|
||||
|
||||
\subsubsection{Structured Programming}
|
||||
Examples of structured programming languages include basically everything except Assembly.
|
||||
\begin{itemize}
|
||||
\item Code is structured.
|
||||
\item \verb|while|, \verb|for|, \verb|if|, \verb|else,| \verb|switch|, \verb|class|, \verb|function|, etc.
|
||||
\item Less emphasis on \verb|GOTO| statements.
|
||||
\item Creating a structure to manage instructions.
|
||||
\item Allows more complex programs to be built.
|
||||
\item Easier to understand.
|
||||
\item Helps to avoid \verb|GOTO| bugs \& spaghetti code.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{The C Programming Language}
|
||||
\textbf{C} is a procedural, imperative, structured ``systems language''.
|
||||
It came into being around 1969-1973 in parallel with the development of the UNIX operating system.
|
||||
Basic Compiled Programming Language (BCPL) \rightarrow B \rightarrow C...
|
||||
C has had an ANSI standard since the 1980s.
|
||||
Now one of the most popular \& powerful languages in use today.
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
// header inclusion: functionally defined in stdio.h is added into the program by the compiler, specifically the Linker step of the compiler
|
||||
// "stdio" is short for "Standard Input / Output"
|
||||
#include <stdio.h>
|
||||
|
||||
// function prototype: tells the compiler that the function exists before it has been implemented
|
||||
// allows the compiler to handle recursion, or functions calling each other
|
||||
void sayHello();
|
||||
|
||||
// function definition: implements the function
|
||||
// note: data type, arguments, return
|
||||
void sayHello() {
|
||||
// calling a function: printf takes a char* argument
|
||||
printf("Hello World!\n");
|
||||
}
|
||||
|
||||
// main function: the entry point to the progam
|
||||
// returns int
|
||||
// takes two arguments: argc (the number of command-line arguments) & argv (an array of the arguments)
|
||||
int main(int argc, char* argv[]) {
|
||||
// calling a function: sayhello takes no argument. nothing is returned
|
||||
sayHello();
|
||||
return 0;
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Example C Program: \texttt{helloWorld.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
#include <stdio.h>
|
||||
|
||||
int add(int a, int b);
|
||||
|
||||
int add(int a, int b) {
|
||||
return a+b;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("Let's add some numbers...\n");
|
||||
int first = 8;
|
||||
int second = 4;
|
||||
printf("The first number is %d\n", first);
|
||||
printf("The second number is %d\n", second);
|
||||
|
||||
// "add" is a function that returns an int
|
||||
// the returned int is stored in the "result" variable - they must have the same data type
|
||||
int result = add(first, second);
|
||||
|
||||
|
||||
// "%d" is for ints - strictly decimal ints
|
||||
// "%i" is any int including octal and hexadecimal
|
||||
printf("When we add them together we get: %d\n", result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
\end{minted}
|
||||
\caption{Example C Program: \texttt{addNumbers.c}}
|
||||
\end{code}
|
||||
|
||||
\subsubsection{Pointers}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
int* p; // variable p is a pointer to an integer value
|
||||
int i; // integer value
|
||||
\end{minted}
|
||||
|
||||
You can \textbf{dereference} a pointer into a value with \verb|*|.
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
// ineger i2 is assigned the integer value that the pointer p is pointing to
|
||||
int i2 = *p;
|
||||
\end{minted}
|
||||
|
||||
You can get a pointer to a value with \verb|&|.
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
// pointer p2 points to the address of integer i
|
||||
int* p2 = &i;
|
||||
\end{minted}
|
||||
|
||||
A function effectively breaking the convention that arguments are not changed in a function is a \textbf{side effect}.
|
||||
This is done by passing addresses.
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
#include<stdio.h>
|
||||
|
||||
void swap(int* x, int* y) {
|
||||
int temp = *x;
|
||||
*x = *y;
|
||||
*y = temp;
|
||||
}
|
||||
|
||||
int main(int argc, char* arv[]) {
|
||||
int a = 8;
|
||||
int b = 4;
|
||||
swap(&a, &b); // this should make a=4 & b=8
|
||||
}
|
||||
\end{minted}
|
||||
|
||||
\subsubsection{Arrays \& Pointers}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
int intArr[5]; // an integer array of size 5
|
||||
// intArr is a pointer to the 0th element of the array - the same as &intArr[0]
|
||||
|
||||
intArr[2] = 3; // same as *(intArr+2) = 3;
|
||||
// (intArr + 2) is of type (int*) while intArr[2] is of type int
|
||||
// in the latter case, the pointer is dereferenced
|
||||
// (intArr + 2) is the same as (&(intArr[2]))
|
||||
// note that the + operator here is not simple addition - it moves the pointer by the size of the type
|
||||
\end{minted}
|
||||
|
||||
\subsubsection{Generic Swap function?}
|
||||
What about a swap function that works on any data type?
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
void swap(void* x, void* y) {
|
||||
void temp = *x; // won't work!
|
||||
// we don't know what size data *x points to, so void temp can't work
|
||||
// it is impossible to have a variable of type void for this reason
|
||||
// but we can have a pointer of type void*
|
||||
|
||||
*x = *y;
|
||||
*y = temp;
|
||||
}
|
||||
\end{minted}
|
||||
\caption{(Non-functional) Attempt at a Generic \texttt{swap} Function}
|
||||
\end{code}
|
||||
\mintinline{c}{void*} is a specific pointer type which points to some location in memory.
|
||||
It has no specific type and therefore no specific size.
|
||||
\\\\
|
||||
\mintinline{c}{sizeof(<type>)} returns the size in bytes of the object representation of \verb|<type>|.
|
||||
\verb|sizeof()| is built-in to the C langauge.
|
||||
\\\\
|
||||
\mintinline{c}{void* memcpy(void* to, const void* from, size_t size)}.
|
||||
The \verb|memcpy()| function copies \verb|size| number of bytes from the object beginning at location \verb|from| into
|
||||
the object beginning at location \verb|to|.
|
||||
The value returned by \verb|memcpy()| is the value of \verb|to|.
|
||||
The \verb|memcpy()| function is defined in \verb|string.h|.
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
#include <string.h>
|
||||
|
||||
void generic_swap(void* vp1, void* vp2, int size) {
|
||||
char temp_buff[size]; // need malloc?
|
||||
memcpy(temp_buff, vp1, size);
|
||||
memcpy(vp1, vp2, size);
|
||||
memcpy(vp2, temp_buff, size);
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Generic Swap Function}
|
||||
\end{code}
|
||||
|
||||
\subsection{Stacks vs Heaps}
|
||||
A \textbf{stack} is a LIFO data structure of limited size \& limited access.
|
||||
It supports only two operations: PUSH \& POP.
|
||||
Stacks are very fast.
|
||||
The limited size of stacks can result in stack overflow, and you cannot free memory in a stack except by POPping.
|
||||
To continue the \verb|swap()| function from above:
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
// first, a, b, & c are pushed onto the stack
|
||||
char a = 'a';
|
||||
int b = 100;
|
||||
int c = 50;
|
||||
|
||||
// when swap() is called, x, y, & temp are pushed onto the stack
|
||||
void swap(int* x, int* y) {
|
||||
int temp = *x;
|
||||
*x = *y;
|
||||
*y = temp;
|
||||
|
||||
// when swap returns, x, y, & temp are popped from the stack and \textbf{their memory is no longer in use}
|
||||
}
|
||||
|
||||
swap(b, c);
|
||||
\end{minted}
|
||||
|
||||
But what if we want to keep track of \verb|temp| and use it later?
|
||||
\\\\
|
||||
A \textbf{heap} is an unordered data structure of (theoretically) unlimited size and global access.
|
||||
The heap operations are allocate \& free.
|
||||
Heaps are slower than stacks.
|
||||
Heaps are also harder to manage than stacks as they can get memory leaks.
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{c}
|
||||
// first, b & c are pushed onto the stack
|
||||
int b = 100;
|
||||
int c = 50;
|
||||
|
||||
// when swap() is called, x, y, & temp are pushed onto the stack
|
||||
void* swap(int* x, int* y) {
|
||||
int temp = *x;
|
||||
|
||||
// we allocate space in memory to perm using malloc
|
||||
int* perm = malloc(int);
|
||||
perm = &temp;
|
||||
x = y;
|
||||
y = *perm;
|
||||
|
||||
// when swap returns, x, y, & temp are popped from the stack
|
||||
// the memory allocated to perm is still in use
|
||||
return perm;
|
||||
}
|
||||
|
||||
|
||||
void* p = swap(b, c);
|
||||
free(p);
|
||||
\end{minted}
|
||||
|
||||
Why not just return \verb|temp| in the same way?
|
||||
\begin{itemize}
|
||||
\item Even when this function terminates, another function can access perm using that pointer.
|
||||
\item If we need to store a large or undeterminable amount of data, we can safely use the heap as there is no risk
|
||||
of stack overflow and no risk of losing reference or accidental de-allocation of memory.
|
||||
\end{itemize}
|
||||
|
||||
\section{Dynamic Memory}
|
||||
FINISH OFF
|
||||
|
||||
\section{Functional Programming}
|
||||
Given the same problem to solve, a program for said problem in any programming language can be considered
|
||||
equivalent to any other at the machine level in that the programs will result in changes to values contained in
|
||||
memory cells.
|
||||
However, there can be quite significant differences at both the conceptual \& implementation level.
|
||||
|
||||
\subsection{Lisp, Racket, \& Scheme}
|
||||
\textbf{LISP} (more commonly referred to as \textbf{Lisp}) is a contraction of \textbf{List Processing}.
|
||||
\textbf{Scheme} is a dialect of Lisp, and \textbf{Racket} is an implementation of Scheme.
|
||||
Lisp uses prefix (Polish) notation, e.g.: \verb|(+ 3 4)|, \verb|(* 5 6)|, \verb|(- 4 (* 5 6))|, etc.
|
||||
|
||||
\subsubsection{Function vs Literal}
|
||||
Parentheses are used to represent a \textbf{function}:
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{lisp}
|
||||
(+ 3 4) ; = 7
|
||||
(* 5 6) ; = 30
|
||||
(- 4 (* 5 6)) ; = -26
|
||||
\end{minted}
|
||||
|
||||
A single quote is used to represent a \textbf{literal}:
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{lisp}
|
||||
(+ 3 4) ; = 7
|
||||
'(+ 3 4) ; = '(+ 3 4)
|
||||
\end{minted}
|
||||
|
||||
Rather than considering \verb|+| as the name of a function, the quote means to take everything literally, i.e.
|
||||
``\verb|+|'' is just a word.
|
||||
Nothing is evaluated.
|
||||
|
||||
\subsubsection{S-Expressions}
|
||||
Both code \& data are structured as nested lists in Lisp.
|
||||
\textbf{Symbolic Expressions} or s-expressions, sexprs, or sexps are a notation for nested list structures.
|
||||
They are defined with a very simple recursive grammar, but produce a very flexible framework for computing.
|
||||
An s-expression is defined as:
|
||||
\begin{enumerate}
|
||||
\item An \textbf{atom}.
|
||||
Atoms are considered to be ``indivisible''.
|
||||
Primitive data types like numbers, strings, booleans, etc. are atoms.
|
||||
Lists \& pairs (s-expressions) are not.
|
||||
\item An expression in the form \verb|(X . Y)|, where \verb|X| \& \verb|Y| are s-expressions.
|
||||
\end{enumerate}
|
||||
|
||||
A \textbf{pair} is two pieces of data together.
|
||||
They are created by the \verb|cons| function, which is short for ``construct'', e.g. \verb|(cons 1 2)|.
|
||||
The two values joined with \verb|cons| are printed between parentheses interspaced by a \verb|.| (a period):
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (cons "banana" "split")
|
||||
'("banana" . "split")'
|
||||
\end{minted}
|
||||
|
||||
\subsection{Lists}
|
||||
A \textbf{list} is an ordered group of data.
|
||||
List elements are separated by a space.
|
||||
The list syntax is a shorthand for an s-expression.
|
||||
Lists are displayed between parentheses using the \verb|'| (single quote character).
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
'(1 2 3) ; list of numbers
|
||||
'("this" "that" "the other") ; list of strings
|
||||
'(1 2 "three" 4) ; list of mixed data types
|
||||
\end{minted}
|
||||
|
||||
Lisp uses nested lists (which are essentially linked lists).
|
||||
We can access the first element of a list using the \verb|car| function:
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (car '(1 2 3))
|
||||
1
|
||||
\end{minted}
|
||||
|
||||
We can access the rest of the list using the \verb|cdr| function:
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (cdr '(1 2 3))
|
||||
'(2 3)
|
||||
\end{minted}
|
||||
|
||||
\verb|cdr| is analogous to \verb|element->rest| in our C linked list.
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (car (cdr '(1 2 3)))
|
||||
2
|
||||
\end{minted}
|
||||
|
||||
There is a shorthand for a combination of \verb|car|s \& \verb|cdr|s (up to 4 operations usually but it depends
|
||||
on the Scheme environment), where \verb|*| is \verb|a| or \verb|d| or a combination (if supported).
|
||||
For example, write a sequence of \verb|car|s \& \verb|cdr|s to extract: ``\verb|d|'' from list
|
||||
\verb|(a b c d e f)| named \verb|lis|:
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{lisp}
|
||||
;;;; these two are equivalent
|
||||
(car (cdr (cdr (cdr lis))))
|
||||
cadddr(lis)
|
||||
\end{minted}
|
||||
|
||||
Lists are really just \verb|cons| pairs where these second element is another list or \verb|empty|;
|
||||
\verb|empty| is a special word, similar to \verb|NULL| in other languages.
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (cons 2 empty)
|
||||
'(2)
|
||||
|
||||
> (cons 1 (cons 2 empty))
|
||||
'(1 2)
|
||||
\end{minted}
|
||||
|
||||
The built-in functions \verb|list| \& \verb|append| provide a more convenient way to create lists.
|
||||
|
||||
\subsubsection{\texttt{define}}
|
||||
\textbf{\texttt{define}} binds a variable to some data.
|
||||
Its format is \mintinline{lisp}{(define variable value)}.
|
||||
\verb|define| is used for user-defined functions.
|
||||
Note that user-defined functions can be used within other use defined functions as long as the functions are
|
||||
defined before they are invoked.
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{lisp}
|
||||
(define (function_name parameter-list)
|
||||
Function-body
|
||||
)
|
||||
|
||||
;;; calculates the absolute addition of two numbers where the function abs returns the absolute value of a number
|
||||
(define (sumabs num1 num2)
|
||||
(+ (abs num1) (abs num2))
|
||||
)
|
||||
\end{minted}
|
||||
\begin{minted}[texcl, mathescape, breaklines, frame=single]{lisp}
|
||||
> (sumabs 2 -3)
|
||||
5
|
||||
\end{minted}
|
||||
|
||||
\subsubsection{\texttt{list} \& \texttt{append}}
|
||||
The \verb|list| function constructs a list from components.
|
||||
It takes the form \mintinline{lisp}{(list el-1 el-2 el-n)}.
|
||||
These components can be symbols, numbers, or lists.
|
||||
\\\\
|
||||
\verb|append| collects components from several lists into one list.
|
||||
Its arguments must be lists.
|
||||
\verb|append| takes the form \mintinline{lisp}{(append list1 list2 listn)}.
|
||||
|
||||
|
||||
|
||||
\end{document}
|
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 144 KiB |