11  Basic concepts of computer science

In ordinary usage (cf. Wikipedia), computer science is the science of the systematic representation, storage, processing, and transmission of information, with particular emphasis on the automatic processing of information by computers. Computer science is both a basic and formal science and an engineering discipline.

Central components of computer science are, on the one hand, computers and, on the other hand, algorithms and programs. Computers are machines that can store data and carry out simple data operations. They perform these simple operations at extremely high speed. The universality of computers rests on the fact that they can store both data and programs.

Programs are algorithms formulated in a programming language. Algorithms, in turn, are sequences of instructions that prescribe certain operations. In algorithms, one distinguishes different levels: the description level, as found, for example, in a recipe, assembly instructions, or a data analysis script; the instructions of an algorithm, such as “mix flour and water”, the pictorial depiction of screwing two parts together, or the instruction x = c(1, 2, 3) in R; and finally the execution level, that is, the actual cooking process, the assembly of a piece of furniture, or the execution of the data analysis script.

Computer science is divided into several subfields that may also be relevant to psychology. Theoretical computer science deals with the foundations of computation, such as automata theory, computability theory, and complexity theory. Applied computer science concerns the development and use of application software, questions of human-computer interaction, and the social effects of computer science. Computer engineering examines the hardware side of computer science, including microprocessor technology, computer architecture, and network technology. Finally, practical computer science deals with concrete implementation in the form of programming, the development and analysis of algorithms, and the construction and use of databases. In psychological basic research, practical computer science is the subfield that is applied most often. However, questions from complexity theory, and thus from theoretical computer science, are also of interest in the development of quantitative psychological theories (cf. Van Rooij & Wareham (2012)), and human-computer interaction is one of the topic areas of modern work psychology (cf. Dahm (2005)).

In addition, there are many special areas of computer science that are closely connected with psychology. These include, in particular, the areas of machine learning and artificial intelligence, which have their origins in quantitative cognitive psychology and today make it possible to analyze large amounts of data automatically and to make predictions. Another important field is computer graphics and visual computing, which deals with image recognition and image synthesis as well as the design of virtual reality (VR) and augmented reality (AR). These technologies use findings from perceptual psychology on the one hand and, on the other hand, open up new possibilities for experiments, therapies, and training in psychology. Computational linguistics contributes methods of speech recognition and speech synthesis that help analyze, model, and make human communication usable in applications such as chatbots or assistance systems. Finally, bioinformatics also plays a major role for psychology, for example by developing methods for evaluating medical imaging procedures that are also used in neuropsychological research and diagnostics.

11.1 Computer architecture

Even though we do not want to go into the technical aspects of computer science in depth here, for imperative programming it is helpful to have at least a rudimentary understanding of the typical physical structure, that is, the hardware, of a computer. A computer consists of several essential hardware components that work together to handle all computation and storage tasks. At the center is the main circuit board, also called the motherboard or mainboard. It is the central printed circuit board on which important components such as the processor, memory, and connectors are linked to one another.

The heart of a computer is the CPU (central processing unit, also called the microprocessor), which combines the arithmetic unit, the control unit, and the registers of the system. It performs all basic computations, controls the flow of data, and coordinates the processes. In addition, it has a small but very fast cache, a volatile memory that temporarily stores frequently needed data in order to speed up processing. Typical examples of current CPUs are Intel Core i processors, AMD Ryzen processors, or Apple Silicon M processors.

The CPU is complemented by RAM (random access memory), the temporary, volatile working memory of the system. During the computer’s runtime, the programs and data currently needed are stored here to enable fast access. RAM is limited in its capacity, for example to 32 to 64 GB in many modern systems.

Mass storage, the computer’s stationary storage, is used for the permanent storage of data and programs. Today, SSDs (solid state drives) are usually used for this purpose; they are much faster and more robust than conventional magnetic hard drives. Cloud storage is also increasingly used as a complement to, or replacement for, local mass storage.

Another important component is the GPU (graphics processing unit), a powerful processor optimized specifically for visualization tasks. The GPU relieves the CPU in graphics-intensive applications such as 3D rendering or video editing and also plays a growing role in computationally intensive tasks such as training neural networks, where it can effectively support the CPU.

The basic functional architecture of modern computers goes back to Von Neumann (1945) and is therefore referred to as the Von Neumann architecture. The decisive characteristic of the Von Neumann architecture is that both instructions (program instructions) and data are stored in the same memory, namely in the form of binary numbers. The central processing unit (CPU) reads both the instructions and the data from this shared memory. As a result, programs can be changed dynamically during execution or even generated anew, since in memory they are present merely as data.

Specifically, Von Neumann (1945) describes the following computer structure: the computer is composed of the basic functional units control unit, arithmetic unit, memory, input unit, and output unit. Programs and data are entered into memory together, so that data, programs, and intermediate and final results are stored in the same memory area. The memory itself is divided into equally sized, numbered (addressed) cells whose contents can be retrieved or changed selectively via the respective address. The instructions of a program typically lie in consecutive memory cells, so that the control unit can call the next instruction simply by increasing the instruction address by one. This principle of sequentially processing instructions is the basic principle of imperative programming and is therefore very closely connected with the Von Neumann architecture. Special jump instructions also make it possible to deviate from this fixed order and jump to another point in the program. The basic instructions include arithmetic operations such as addition and multiplication, logical comparisons such as logical AND or OR, and transport instructions by which data are transferred, for example, from the input unit to memory or from memory to the arithmetic unit. All data, that is, both instructions and addresses, are encoded in binary. A suitable circuit technology in the computer handles the binary encoding and decoding of the information.

11.2 Algorithms and programs

A real-world problem denotes the problem that is to be solved with the help of a computer. A typical example is the evaluation of questionnaire data from a psychological study. To work on such a problem, one first creates a problem specification, that is, the precise verbal formulation of the real-world problem, as one might find it in the methods section of a scientific publication. On this basis, an algorithm is designed, that is, a sequence of instructions for solving the problem, for example reading in the data, computing descriptive statistics, and carrying out a statistical test. Finally, the algorithm is implemented in the form of a program, that is, as a text file written in a programming language that can be executed by a computer.

Definition 11.1 (Algorithm) An algorithm is a sequence of instructions for deriving certain output data from certain input data, where the following conditions must be satisfied:

  • Finiteness. The sequence of instructions is completely described in a finite text.
  • Effectiveness. Each instruction must actually be executable.
  • Termination. The algorithm ends after finitely many instructions.
  • Determinacy. The course of the algorithm is prescribed at every point in time.

If \(E\) denotes the set of admissible input data and \(A\) denotes the set of admissible output data, then an algorithm is therefore a function: \[\begin{equation} f: E \to A, \quad e \mapsto f(e). \end{equation}\] Conversely, functions that can be described by an algorithm are called computable functions.

11.3 Programming languages

Programming languages specify the rules that a program must obey. They define a syntax, that is, the vocabulary and structure of a program, as well as the semantics, that is, the meaning of the permitted instructions. In the following, we give some terminological clarifications for categorizing different forms of programming languages. Here one distinguishes machine language and so-called higher-level programming languages, different generations of programming languages, the imperative programming that is the focus here from declarative programming, and compiled from interpreted programming languages.

Machine language and higher-level programming languages

Machine language consists of elementary operation instructions such as storing, comparing, or adding, which are encoded as binary numbers. Table 11.1 shows some examples of such instructions:

Table 11.1: Examples of machine language
Instruction Binary code
Add contents of R1 to contents of R2 1001 0010
Increase contents of R by 1 1001 0110
Transfer contents of R1 to R3 0010 0011

Programs written in machine language are called machine programs. De facto, a computer executes only machine programs, because these are understood directly by the hardware. For humans, however, programming in machine language is very laborious, which is why higher-level programming languages are predominantly used in practice.

Higher-level programming languages use words and sentences modeled on human language to make programming more understandable and efficient. Programs in higher-level programming languages are then translated into machine language by a compiler or an interpreter so that they can be executed by the computer. Well-known examples of higher-level programming languages are FORTRAN, COBOL, C++, Python, and R.

Generations of programming languages

The first-generation programming languages (1GL) are the machine languages. Programs are written directly in the form of binary numbers, for example 10110000 01100001, which corresponds to B0 61 in hexadecimal notation. Such machine languages were used mainly on the first vacuum-tube and relay computers such as ENIAC or early IBM mainframes of the 1940s and early 1950s.

With the second generation (2GL), assembly languages emerged from the 1950s on as the first form of symbolic programming. Here, human-readable abbreviations are used, although they are still processor-specific. An example of such a processor-specific Intel instruction is MOV AL, 61H. This generation of programming languages was used mainly on mainframe computers such as the IBM 704 or early mainframes of the 1950s and 1960s, which already used transistors instead of vacuum tubes.

From the 1970s onward, the third-generation programming languages (3GL) became established. These include higher-level programming languages such as FORTRAN, C, C++, and Java. They are much more programming-friendly and, above all, processor-independent, which considerably improves the portability of programs. Such languages were first used widely on minicomputers such as the DEC PDP-11 and later on workstations, PCs, and servers.

Finally, from the 1980s onward, the fourth-generation programming languages (4GL) developed; they include languages such as Python, MATLAB, and R. These are characterized by minimizing code overhead, high flexibility, support for multiparadigmatic approaches, and a high degree of automation. They are used mainly on modern PCs, high-performance computing clusters (HPC clusters), and cloud-based systems, which became increasingly available from the 1990s onward.

Imperative and declarative programming

In imperative programming (from imperare, Latin for “to command”), the path to a solution is specified as a sequence of instructions or commands. These commands process data that are addressed via variables. Within imperative programming, two different approaches are distinguished: procedural imperative programming and object-oriented imperative programming.

In procedural imperative programming, data and the commands that manipulate them are treated separately. The central structural concept here is procedures or functions, which encapsulate reusable units of instructions. By contrast, in object-oriented imperative programming, data and the commands that act on them are combined as objects. These objects are at the same time data carriers and providers of functions and form the central structural concept of this approach.

In practice, mixed forms of the two paradigms are often used, because the approaches can be combined well depending on the problem. An example of such a mixed form is the newer programming language Julia, which does not offer a classical object-oriented approach but instead a multiple-dispatch approach: functions can have different implementations depending on the types of their arguments and can thus combine aspects of object-oriented and procedural programming.

In declarative programming, the focus is not on the path to the solution but on the description of the problem itself. The programmer formulates what is to be achieved, and not in detail how the computer is to reach this goal step by step. The path to the solution is determined automatically by the system on the basis of given rules and mechanisms.

A typical feature of declarative approaches is that the focus is on defining relations, conditions, or goals rather than on an explicit sequence of commands. The programmer thus describes the desired result in a formal language, and the computer independently searches for a solution that satisfies these conditions.

Well-known approaches to declarative programming are, for example, logic programming, as implemented in the language Prolog, or functional programming, as in Haskell. In logic programming, facts and rules are formulated from which the system draws conclusions by inference. In functional programming, computations are described by the composition of functions, while the state of the data is not changed explicitly. Declarative programming is particularly suitable for problems in which the path to the solution can be complex and varied, but the desired properties of the result can be clearly specified. Typical application areas include database queries (for example SQL), knowledge bases, constraint solving, and formal proofs.

A modern example of the use of declarative programming is the system Lean, a so-called theorem prover. Lean is used to formally prove mathematical theorems. Here the user formulates the axioms, definitions, and statements to be proved in a mixture of declarative and interactive commands. The system automatically checks whether the specified steps are valid and completes the missing parts of the proof according to fixed rules. In this way, Lean combines declarative programming with automated proof and makes it possible to develop large mathematical theories in a formally correct way. For an introduction to Lean in the context of mathematical proof, see, for example, The Mechanics of Proof.

Compiled and interpreted programming languages

Compiled programming languages are characterized by the fact that the entire source code is translated into machine language before execution. A special program, the so-called compiler, is used for this translation. The machine code generated by the compiler is executed directly by the processor, which enables the program to run very quickly. Since the executable program is already available in machine language, it no longer has to be translated at runtime. However, whenever the source code is changed, it is necessary to compile the program again. Typical examples of compiled programming languages are C, C++, and Rust.

By contrast, in interpreted programming languages, the source code is translated into a machine-like language during execution. This is done by a special program, the so-called interpreter. Since translation and execution take place at the same time, execution is usually slower than in compiled programs. One advantage, however, is that after changes no separate compilation step is required; the program can be executed immediately. Typical examples of interpreted programming languages are Python and R.

In the sense of the concepts introduced here, R is an interpreted multiparadigmatic fourth-generation programming language that supports object-oriented concepts but is often used procedurally and functionally and is tailored to statistical data analysis.

11.4 R

R is a programming language and a software package originally developed by Ross Ihaka (Ihaka & Gentleman (1996)). It is a free dialect of the proprietary software S, introduced by Becker et al. (Becker et al. (1988)). Today, R is developed and maintained by the R Core Team and the R Foundation. R has a large and active community that has so far contributed more than 20,000 R packages, which extend the system with numerous additions. The core of R deliberately develops in an evolutionary and conservative way, while the large number of R packages enables consistent and progressive further development. R can be easily downloaded and installed from CRAN.

With R, one can load, manipulate, and save data sets in different formats. The language makes it possible to carry out a wide variety of computations on different data structures. In addition, R offers a broad spectrum of statistical analysis methods that can be applied directly to the data. It is also typical to write reproducible data analysis scripts with which analyses can be automated and results can be documented transparently. Finally, R is very well suited for creating figures and visualizations in order to present data clearly.

However, some things that are important in psychological data science can be implemented only to a limited extent with R. For example, R per se lacks a modern and appealing development environment of its own; embedding R in VS Code remedies this. In the area of scientific computing, R reaches its limits. Here, languages such as Python, MATLAB, or Julia often offer more powerful and flexible tools. Finally, R is hardly suitable for programming psychological experiments, for which Python or MATLAB are also more commonly used.

There are many ways to get help with R. Quite classically, a quick search on Google, a request to ChatGPT, or an interaction with Copilot in VS Code often already helps. During programming, and when one already knows the name of a function, one can call help directly in R:

?mean
help(mean)

For more comprehensive, longer tutorials and background information, it is worth taking a look at the so-called vignettes of installed packages:

browseVignettes()

In addition, there are numerous helpful online resources, for example the specialized search engine rseek or the many articles and guides on R-Bloggers, although most information on these platforms is certainly also part of the databases accessible through chatbots such as ChatGPT, Gemini, Claude, or DeepSeek.

11.5 Visual studio code

Visual Studio Code (VS Code) is a free source code editor from Microsoft that has been available for Windows, macOS, and Linux since 2015. Roughly speaking, VS Code is something like the perhaps better-known RStudio environment, except that it is for all programming languages. VS Code can be flexibly extended through extensions and used as an integrated development environment (IDE) for almost any language, for example R, Python, Julia, Shell, Quarto, and many more. Since 2018, VS Code has been regarded as the most popular editor overall, as annual surveys show. Remarkably, this means that a Microsoft product in particular is also the most-used editor in the Linux world. VS Code is highly configurable, strongly shaped by the community, and enables the synchronization of settings and extensions across multiple devices via Microsoft’s GitHub. Detailed documentation with instructions for use can be found at www.code.visualstudio.com/docs. The corresponding documentation R in Visual Studio Code provides an introduction to working with R in VS Code.

Becker, R. A., Chambers, J. M., & Wilks, A. R. (1988). The new S language: A programming environment for data analysis and graphics (Reprint). Chapman & Hall.
Dahm, M. (2005). Grundlagen der Mensch-Computer-Interaktion. Pearson Studium.
Ihaka, R., & Gentleman, R. (1996). R: A Language for Data Analysis and Graphics. Journal of Computational and Graphical Statistics, 5(3), 2999–2314.
Van Rooij, I., & Wareham, T. (2012). Intractability and approximation of optimization theories of cognition. Journal of Mathematical Psychology, 56(4), 232–247. https://doi.org/10.1016/j.jmp.2012.05.002
Von Neumann, J. (1945). First draft of a report on the EDVAC. Moore School of Electrical Engineering, University of Pennsylvania. https://doi.org/10.5479/sil.538961.39088011475779