Introduction
To better understand the contents introduced in the course, it helps to take a brief look at at some fundamental computer science concepts.
Algorithms
Algorithms are clearly formulated instructions for solving problems; they form the basis of all programming activities. A problem is usually stated in terms of an input state, which is to be transformed into an output state (e.g., we might start with some numbers as the input and seek the sum of these numbers as an output). Algorithms can be formulated in natural language, in pseudocode, or in a programming language like Python.
Classic examples of algorithms formulated in natural language are cooking recipes. Computer science students typically spend lots of time analyzing searching algorithms and sorting algorithms. In the legal domain, the construction of a legal opinion on a given case might be seen as an example of an algorithm.
Every language consists of syntax (describing the structure of well-formed statements in the language) and semantics (describing the meaning of well-formed statements). A programming language is a formal language.
Complexity
Instead of just looking for a solution to a problem, we strive to find a resource-efficient solution, i.e., an algorithm that solves our problem as fast as possible, using as little memory as possible. Larger inputs typically require more computing resources than smaller inputs - but we're looking for an efficient solution applicable to many inputs of potentially different sizes. Thus, we are interested in the relationship between input size and resource consumption, where the resources of interest are time (time complexity) and memory (space complexity).
For a sorting problem, we might ask how the sorting algorithm in question behaves when we double the length of the input (e.g., a list). Does it need more computation steps than before? If so, does the number of steps double or triple, i.e., do we observe linear growth? Or does the number of steps needed to sort the input grow exponentially? The question is also relevant for law students - for example, if they are tasked with sorting corrected exams alphabetically for redistribution to their peers.
The concrete specification of an algorithm in a programming language is called implementation. There are often many possible solutions to implement an algorithm, and the resulting implementations can differ drastically in terms of their time complexity and space complexity.
Abstraction
Abstraction is one of the main pillars of programming. To solve an apparently difficult problem, we abstract from certain single steps when formulating our algorithms. For example, as part of a sorting algorithm, we might code 'pick the smallest element from the list' without detailing how to find the smallest element.
This kind of abstraction is also important for the functioning of a computer: A processor consists of simple electronic switching components, which only know the states power on and power off. Due to clever wiring, these components can be used to interpret numbers or execute calculations. To enable these operations, the processor offers a set of basic instructions, the so-called machine code. Vendors normally offer a program called assembler that translates certain human-readable instructions into machine code. Since the set of assembler instructions is too restricted for today's applications, the operating system (OS, e.g., Windows, macOS, or a Linux system) bridges the gap between applications and assembler code. An operating system is (partly) written in assembler code, and it abstracts from many sequences of assembler instructions. It provides programs executed within the operating system with more complex functions, e.g., access to files or to network connections.
Finally, higher programming languages let us write complex applications using easily comprehensible syntax and semantics. Higher programming languages typically build on the functions of the operating system; examples of such languages are C++, Java, and Python. Programs in higher programming languages must be translated into machine code to be executed. This translation can be effected by a compiler before the program is executed (compilation) but it can also be done by an interpreter while the program is executed (interpretation).