exam-monwed-class
prolog syntax
% rules for factorial using recursion factorial(0, 1). factorial(N, Result) :- N > 0, N1 is N - 1, factorial(N1, Result1), Result is N * Result1. % rules for absolute value using conditional absolute(X, AbsX) :- X < 0, AbsX is -X. absolute(X, X).
SML (ML) syntax
(* Binding Values *) val x = 10; (* Function Definition *) fun add(x, y) = x + y; (* Pattern Matching *) fun factorial 0 = 1 | factorial n = n * factorial (n - 1); (* Conditional Expression *) fun absolute x = if x < 0 then ~x else x; (* Tuples *) val pair = (1, "hello"); (* Lists *) val myList = [1, 2, 3, 4]; (* Anonymous Functions (Lambda) *) val square = fn x => x * x; (* Higher-Order Functions *) fun applyTwice(f, x) = f (f x); (* Type Annotations *) fun add (x: int, y: int): int = x + y; (* Datatypes *) datatype 'a option = NONE | SOME of 'a; (* Record Types *) type person = {name: string, age: int}; (* List Functions (Standard Library) *) val length = List.length [1, 2, 3]; (* Map Function *) val squaredList = List.map (fn x => x * x) [1, 2, 3, 4]; (* Filter Function *) val evens = List.filter (fn x => x mod 2 = 0) [1, 2, 3, 4]; (* Let Expressions *) val result = let val x = 5; val y = 10; in x + y end;
test question: given the following set of Prolog facts/rules: 1. fact (a, b) . 2. fact (b, c) . 3. fact (c, d) . 4. fact (c, e) . 5. fact (d, e) . 6. funFact (X, Y) :- fact (X, Y) . 7. funFact (X, Y) :- fact (X, Z), funFact (Z, Y) indicate whether the following function "queries" resolve to true or false and indicate result where applicable. 1. ?- fact (x, y) . 2. ?- fact (a, b) . 3. ?- fact (c, Result) . 4. ?- funfacts (a, e) . 5. ?- funfacts (Result, d) . 6. ?- funfacts (d, Result) .
1. false - no matching facts 2. true - direct fact 3. Result = d; Result = e; because there are two potential matching facts 4. true - can be derived with rule #6 5. Result = c; Result = a; can be derived with rule #7 6. false - no matching facts
how to evaluate a function's type
1. find the input type 2. find the output type 3. ans = "i-type -> o-type"
8 major categories of control flow
1. sequential 2. conditional 3. iterative/loop 4. transfer 5. procedural 6. concurrent 7. exceptional 8. co-routine
scheme syntax
; Define (for binding values) (define x 5) ; Let (for local bindings) (define result (let ((y 10) (z 20)) (+ x y z))) ; Lambda (for defining anonymous functions) (define square (lambda (a) (* a a))) ; Car and Cdr (for accessing elements of a list) (define myList '(1 2 3)) (define firstElement (car myList)) (define restOfList (cdr myList)) ; Cons (for constructing lists) (define newList (cons 0 myList)) ; Null? (for checking if a list is empty) (define isListEmpty (null? '())) ; If (for conditional branching) (define (abs-value x) (if (< x 0) (- x) x))
test question: the following statement will produce a "syntax error" in F#. Why? let f x = if x > 0 then "The value is Positive"
Because it is missing an else token/branch. In F#, an if statement must have then and else
test question: assume a "pseudo-Java/C" language that DOES NOT employ short-circuit evaluation of Boolean expressions. What would be the output of the following fragment upon invoking main( ) ? boolean fun1 () { println("Fun1"); return false; } boolean fun2 () { printIn("Fun2"); return true; } void main() {println( fun1() && fun2() ); }
Fun1 Fun2 false (both functions executed because lack of short-circuit evaluation, evaluates to false at the end because of the &&
Scheme
Lanuage of Lisp dialect, uses prefix notation where the operator precedes its operands
test question (T/F): in functional programming languages like OCaml/F#, a function requiring more than one parameter will accept only one parameter and yield a new function to accept the next parameter
True
test question: what output would be produced by the execution of the following mini-program: let rec mystery lst = match lst with | x :: y :: XS -> if x=y then mystery (x :: xs) else x :: mystery (y :: xs) | x -> x printfn "Result: %A" (mystery [1;1;2;3;4;4;4;5])
[1; 2; 3; 4; 5]. The mystery function removes consecutive duplicates from the list.
unification
a process where variables are bound to values based on logical relationships
how to make array address calculations
address=base address+(index×element size)
algebraic data types vs. class-based types
algebraic data types represent data structures by combining simpler types, while class-based types use classes and objects for data representation
referential transparency
an expression can be replaced with its value without changing the program's behavior
map
applies a function to each elm of a list (define squaredList (map (lambda (x) (* x x)) '(1 2 3))) ; Results in '(1 4 9)
programming paradigm
approach to programming that guides the way developers design, structure, and organize their code
:: operator
called the cons operator, used to construct a new list by adding an elm to the front of an existing list
list concatenation
combines 2 or more lists (define concatenatedList (append '(1 2) '(3 4))) ; Results in '(1 2 3 4)
JVM
compiler that is just as fast as C/C++
xs
conventional name you'll see in programs to represent the remaining elms of a list element :: existingList let myList = 1 :: [2; 3; 4] // myList is now [1; 2; 3; 4]
precedence
crucial for determining the order in which different operations or statements are executed
Prolog
declarative programming language designed for tasks involving rule-based logic programming (uses logical paradigm)
lazy evaluation (AKA normal order evaluation)
delaying the evaluation of an expression until its value is actually needed, can improve efficiency by only computing what is necessary
declarative programming (paradigm)
describe what should be done, the desired outcome, and not how to achieve it (SQL, HTML)
type abstraction
details about data type are hidden, exposing only the essential characteristics. achieved through abstract data types or classes
applicative order
evaluates function arguments before applying them
control abstraction
expressing control flow structures in a way that hides low-level details from the programmer. achieved through higher-order functions
test question (T/F): individual programming languages may be classified as either object-oriented or functional but never have features of both paradigms, as this would create ambiguities for compilers.
false, many modern programming languages are multi-paradigm. Languages like Scala, Kotlin, and Swift are considered both object-oriented and functional
test question (T/F): in object-oriented languages such as Java, the compiler can verify that every variable is properly initialized to a non-null value before its first use in an expression.
false, while some modern languages, including Java, require variables to be initialized before use, the compiler does not always check for null values. It is the responsibility of the programmer to ensure proper initialization to avoid null pointer exceptions during runtime
optionals
feature designed to handle the absence of a value
pattern matching
feature in functional programming languages that allows you to destructure and match complex data structures, such as lists. It simplifies code by handling different cases based on the structure of the data Ex: (define (classify-value x) (match x ((? number?) 'numeric) ((? string?) 'text) (else 'unknown)))
tail recursion
form of recursion where the recursive call is the last operation in the function
functional programming
formal definition: programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data digestible definition: coding style where functions are first-class citizens, which means they can be assigned to variables, passed as arguments, and returned as values. emphasizes immutability, pure functions, and declarative programming.
test question (multiple choice): given the OCaml/F# function: let rec f = function | [] -> [] | x :: xs -> (x, x+1) :: f xs. What does the following method call yield: f [2; 4; 6]? a) [] b) [12] c) [4; 8; 12] d) [(2,3)(4,5)(6,7)]
function: used to pattern match on the input argument [] -> []: matches an empty list x :: xs -> (x, x+1) :: f xs: matches a non-empty list with a head element x and a tail xs. It then creates a tuple (x, x+1) and appends it to the result of the recursive call f xs. 1. 2 -> (2,3) 4 -> (4,5) 6 -> (6,7) 2. Combine results and you get d) [(2,3)(4,5)(6,7)]
Haskell
functional programming language, uses lazy evaluation by default, strong emphasis on immutability, referential transparency, and type safety
high-order functions
functions that can take other functions as arguments or return functions as results
pure functions
functions that produce the same output for the same input and have no side effects
constructors
functions used to create and initialize objects or instances of a class in object-oriented programming
role of heaps and stacks
heap: memory management/ dynamic memory allocation stack: function call management and local variables
C language
high-level assembler, abstracts you away from the implementation details of any particular CPU but allows for programming that is still close to the "bare metal" of the machine. It is almost impossible to understand how operating systems work without understanding the C programming language
left-associativity
if operators are left-associative, they are evaluated from left to right when they have the same precedence. Most operators in programming languages, such as addition (+) and subtraction (-), are left-associative. in the expression a - b - c, the subtraction operations are performed from left to right
right associativity
if operators are right-associative, they are evaluated from right to left when they have the same precedence the exponentiation operator (^ or **) in some languages
test question (multiple choice): what is the function type of f in the declaration: let f = fun x y -> (x+1, y+1.0). a) 'a * 'b -> 'a * 'b b) int * float -> int * float c) int -> float -> int * float d) (int, float)
int -> float -> int * float the function f takes two parameters, an integer (x) and a float (y), and returns a tuple of type int * float
test question (multiple choice): what is the function type of f in the declaration: let f x = x + 1. a) int b) int -> int c) fun x y d) x*y
int -> int the function f takes an integer (x) as input and returns an integer (x + 1)
test question: write a function called valueRange in OCaml/F# which: - accepts a function/predicate (that acts as a "filter") and a list of integers - resolves to a pair of values (a tuple) containing both the largest and smallest values in the list which are also "accepted" by the function
let valueRange filter lst = let rec find_range acc_min acc_max = function | [] -> (acc_min, acc_max) | hd :: tl -> if filter hd then find_range (min acc_min hd) (max acc_max hd) tl else find_range acc_min acc_max tl in match lst with | [] -> failwith "Empty list has no range" | hd :: tl -> find_range hd hd tl
recursion
looping is replaced by recursion in functional programming. functions are designed to call themselves with modified arguments
scripting languages
much slower in execution speed than Java or C, elaborate functionality in just a few lines of code python, perl, ruby, groovy
Racket
multi-paradigm programming language that supports functional, imperative, and logic programming, often used for educational purposes and the development of domain-specific languages
test question (multiple choice): what is the resulting data type of the evaluation of the expression: 2 * 3 = 4 a) int b) bool c) int -> int d) none (syntax error)
none (syntax error) the = operator is used for comparing values in Ocaml/F#, not for assignment. the way to compare the value of 2*3 with 4 would be : let result = 2 * 3 in result = 4
immutability
once data is created, it cannot be changed. Instead of modifying existing data structures, functional programming encourages creating new ones.
structuring and destructuring
organizing and breaking down complex data structures into simpler components
test question (multiple choice): nearly all language constructs in the Scheme language are represented in: a) infix notation b) prefix notation d) a mix
prefix notation
short-circuit evaluation
programming language feature where the evaluation of a logical expression is stopped as soon as the result is determined
Object Oriented Programming
programming paradigm that uses objects (instances of classes) to organize code. objects model real-world entities and encapsulate data and behavior
car
returns the first elm of a list
cdr
returns the rest of the list (all elms but first)
functional programming languages include
scala, f#, Ocaml, SML
dont forget to add programming languages
scala, kotlin, swift,
lisp dialects
scheme, racket, lisp, clojure
filter
selects elms from a list based on a predicate (define evens (filter even? '(1 2 3 4))) ; Results in '(2 4)
test question: in a language (like C, for example) the memory for arrays (of any dimension) is allocated in a contiguous, "row-major" block of memory. Assume an int consists of two bytes, each represented as a single address in memory. Given an array declared as int someArray [10] [10]. If the base (starting) address of someArray is memory address 1000, what memory address refers to the element someArray [3] [2]?
starting address: 1000 size of each row: 10*2=20 bytes (for 10 integers) size of each elm: 2 bytes addr of someArray[3][2] = starting address + (row index * size of each row) + (column index * size of each element) addr = 1000+(3*20)+(2*2) = 1064 ans: 1054
Scala
statically typed, functional programming language that runs on the Java Virtual Machine (JVM) and combines object-oriented and functional programming paradigms features concise syntax, pattern matching, and immutable collections
Swift
statically-typed programming language developed by Apple for app development, known for its type safety, modern syntax, optionals, and emphasis on performance
Kotlin
statically-typed programming language developed by JetBrains, interoperable with Java, featuring concise syntax, null safety, extension functions, and aimed at enhancing developer productivity for Android and general-purpose development
imperative programming (paradigm)
step-by-step instructions to achieve a specific result (C++, C, Java)
structured flow vs unstructured flow
structured flow: involves organized, modular programming constructs unstructured flow: lacks such organization and can lead to spaghetti code
multi-paradigm language
supports features from multiple programming paradigms
rust
systems programming language known for its focus on memory safety, zero-cost abstractions, and concurrency support
currying
technique where a function that takes multiple arguments is transformed into a sequence of functions, each taking a single argument. in functional programming, a function that appears to take multiple parameters is often curried. when you provide fewer arguments than the function expects, it returns a new function that expects the remaining arguments.
types in OCaml/F#
the OCaml/F# language supports strong static typing with type inference, enabling the compiler to deduce types without explicit declarations.
polymorphism
the ability of a variable, function, or operator to take on multiple forms or behaviors
associativity
the order in which operators of the same precedence are evaluated in the absence of parentheses
initialization
the process of assigning initial values to variables or data structures
boxing
the process of converting a value type to a reference type (unboxing: the reverse, converting a reference type to a value type)
assignment (=)
the process of giving a value to a variable
orthagonality
the property that a relatively small set of primitive constructs can be combined in a relatively small number of ways to build the control and data structures of a language An orthogonal language has features that can be freely combined without restrictions
assembly language
there is no better way to understand the hardware that runs the software than by programming in assembly
test question (T/F): whereas classes can be used to implement "type abstraction," in OO languages, high...functions can be used to implement "control abstraction" in functional languages
true
test question (T/F): In Prolog, a function's formal parameters may serve as both "inputs" and "outputs."
true, formal parameters are often used bidirectionally. Predicates in Prolog can have input, output, or both input and output parameters. The values of parameters are determined by unification
test question (T/F): unlike the arithmetic operators in most programming languages, Scheme arithmetic operators do not have explicit precedence associated with them
true, in Scheme, a Lisp dialect, arithmetic expressions are typically written in prefix notation, and there is no need for explicit precedence rules. Instead, Scheme uses parentheses to indicate the order of operations. For example, instead of 3 + 5 * 2, you would write (+ 3 (* 5 2)).
test question (T/F): if a language uses "normal order evaluation," it's possible that g(x) may never be evaluated in an expression such as in result = f(x, g(x)).
true, lazy evaluation delays the evaluation of an expression until its value is actually needed. If the value of g(x) is not used in the computation of result, it may never be evaluated. Haskell, a functional programming language, uses lazy evaluation by default.
test question (T/F): in languages that implement tail recursion, it is generally best to avoid recursion altogether for better performance and to avoid potential stack overflows
true, some programming languages optimize tail-recursive calls by reusing the current function's stack frame, reducing the risk of a stack overflow. However, not all languages or compilers support this, and using iteration may be preferable for performance
test question (multiple choice): which is not one of the 8 major categories of control flow? a) sequencing b) selection c) iteration d) variable declaration e) recursion f) exception handling
variable declaration is related to variable and data type handling, not directly a category of control flow
statically typed
variable types are checked at compile-time, reducing the likelihood of runtime errors
fsharp syntax
variables: let x = 42 functions: let add x y = x + y pattern matching: match x with | 0 -> "Zero" | 1 -> "One" | _ -> "Other" list creation: let numbers = [1; 2; 3; 4; 5] recursion: let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1) higher-order functions: let square_all lst = List.map (fun x -> x * x) lst option type: let divide x y = if y = 0 then None else Some (x / y) record type: type person = { name: string; age: int } let john = { name = "John"; age = 30 } list comprehension: let evens = [for x in [1; 2; 3; 4; 5] do if x % 2 = 0 then yield x]
ocaml syntax
variables: let x = 42;; functions: let add x y = x + y;; pattern matching: match x with | 0 -> "Zero" | 1 -> "One" | _ -> "Other" list creation: let numbers = [1; 2; 3; 4; 5];; recursion: let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1);; higher-order functions: let square_all lst = List.map (fun x -> x * x) lst;; option type: let divide x y = if y = 0 then None else Some (x / y);; record type: type person = { name: string; age: int };; let john = { name = "John"; age = 30 };; list comprehension: let evens = List.filter (fun x -> x mod 2 = 0) [1; 2; 3; 4; 5];; (* Lambda function that squares its argument *) let square = fun x -> x * x;;