📚 The CoCalc Library - books, templates and other resources
cocalc-examples / martinthoma-latex-examples / documents / Programmierparadigmen / Programmiersprachen.tex
132930 viewsLicense: OTHER
%!TEX root = Programmierparadigmen.tex1\chapter{Programmiersprachen}2\begin{definition}\xindex{Programmiersprache}\xindex{Programm}%3Eine \textbf{Programmiersprache} ist eine formale Sprache, die durch eine4Spezifikation definiert wird und mit der Algorithmen beschrieben werden5können. Elemente dieser Sprache heißen \textbf{Programme}.6\end{definition}78Ein Beispiel für eine Sprachspezifikation ist die \textit{Java Language Specification}.\footnote{Zu finden unter \url{http://docs.oracle.com/javase/specs/}} Obwohl es kein guter Stil ist, ist auch9eine Referenzimplementierung eine Form der Spezifikation.1011Im Folgenden wird darauf eingegangen, anhand welcher Kriterien man12Programmiersprachen unterscheiden kann.1314\section{Abstraktion}15Wie nah an den physikalischen Prozessen im Computer ist die Sprache?16Wie nah ist sie an einer mathematisch / algorithmischen Beschreibung?1718\begin{definition}\xindex{Maschinensprache}\xindex{Befehlssatz}%19Eine \textbf{Maschinensprache} beinhaltet ausschließlich Instruktionen, die direkt20von einer CPU ausgeführt werden können. Die Menge dieser Instruktionen21sowie deren Syntax wird \textbf{Befehlssatz} genannt.22\end{definition}2324\begin{beispiel}[Maschinensprachen]25\begin{bspenum}26\item \xindex{x86}x8627\item \xindex{SPARC}SPARC28\end{bspenum}29\end{beispiel}3031\begin{definition}[Assembler]\xindex{Assembler}%32Eine Assemblersprache ist eine Programmiersprache, deren Befehle dem33Befehlssatz eines Prozessor entspricht.34\end{definition}3536\begin{beispiel}[Assembler]%37Folgendes Beispiel stammt von \url{https://de.wikibooks.org/wiki/Assembler-Programmierung_für_x86-Prozessoren/_Das_erste_Assemblerprogramm}:38\inputminted[linenos, numbersep=5pt, tabsize=4, frame=lines, label=firstp.asm]{nasm}{scripts/assembler/firstp.asm}39\end{beispiel}4041\begin{definition}[Höhere Programmiersprache]\xindex{Programmiersprache!höhere}%42Eine Programmiersprache heißt \textit{höher}, wenn sie nicht ausschließlich43für eine Prozessorarchitektur geschrieben wurde und turing-vollständig ist.44\end{definition}4546\begin{beispiel}[Höhere Programmiersprachen]47Java, Python, Haskell, Ruby, TCL, \dots48\end{beispiel}4950\begin{definition}[Domänenspezifische Sprache]\xindex{Sprache!domänenspezifische}%51Eine domänenspezifische Sprache (engl. domain-specific language; kurz DSL)52ist eine formale Sprache, die für ein bestimmtes Problemfeld53entworfen wurde.54\end{definition}5556\begin{beispiel}[Domänenspezifische Sprache]57\begin{bspenum}58\item HTML59\item VHDL60\end{bspenum}61\end{beispiel}6263\section{Paradigmen}64Eine weitere Art, wie man Programmiersprachen unterscheiden65kann ist das sog. \enquote{Programmierparadigma}, also die Art wie66man Probleme löst.6768\begin{definition}[Imperatives Paradigma]\xindex{Programmierung!imperative}%69In der \textit{imperativen Programmierung} betrachtet man Programme als70eine Folge von Anweisungen, die vorgibt auf welche Art etwas71Schritt für Schritt gemacht werden soll.72\end{definition}7374\begin{beispiel}[Imperative Programmierung]75In folgenden Programm erkennt man den imperativen Programmierstil vor allem76an den Variablenzuweisungen:77\inputminted[numbersep=5pt, tabsize=4]{c}{scripts/c/fibonacci-imperativ.c}78\end{beispiel}7980\begin{definition}[Prozedurales Paradigma]\xindex{Programmierung!prozedurale}%81Die prozeduralen Programmierung ist eine Erweiterung des imperativen82Programmierparadigmas, bei dem man versucht die Probleme in83kleinere Teilprobleme zu zerlegen.84\end{definition}8586\begin{definition}[Funktionales Paradigma]\xindex{Programmierung!funktionale}%87In der funktionalen Programmierung baut man auf Funktionen und88ggf. Funktionen höherer Ordnung, die eine Aufgabe ohne Nebeneffekte89lösen.90\end{definition}9192\begin{beispiel}[Funktionale Programmierung]93Der Funktionale Stil kann daran erkannt werden, dass keine Werte zugewiesen werden:94\inputminted[numbersep=5pt, tabsize=4]{haskell}{scripts/haskell/fibonacci-akk.hs}95\end{beispiel}9697Haskell ist eine funktionale Programmiersprache, C ist eine98nicht-funktionale Programmiersprache.99100Wichtige Vorteile von funktionalen Programmiersprachen sind:101\begin{itemize}102\item Sie sind weitgehend (jedoch nicht vollständig) frei von Seiteneffekten.103\item Der Code ist häufig sehr kompakt und manche Probleme lassen104sich sehr elegant formulieren.105\end{itemize}106107\begin{definition}[Logisches Paradigma]\xindex{Programmierung!logische}%108Das \textbf{logische Programmierparadigma} baut auf der formalen Logik auf.109Man verwendet \textbf{Fakten} und \textbf{Regeln}110und einen Inferenzalgorithmus um Probleme zu lösen.111\end{definition}112113Der Inferenzalgorithmus kann z.~B. die Unifikation nutzen.114115\begin{beispiel}[Logische Programmierung]116Obwohl die logische Programmierung für Zahlenfolgen weniger geeignet erscheint,117sei hier zur Vollständigkeit das letzte Fibonacci-Beispiel in Prolog:118\inputminted[numbersep=5pt, tabsize=4]{prolog}{scripts/prolog/fibonacci.pl}119\end{beispiel}120121\section{Typisierung}122Programmiersprachen können anhand der Art ihrer Typisierung unterschieden werden.123124\begin{definition}[Typisierungsstärke]\xindex{Typisierungsstärke}%125Es seien $X, Y$ Programmiersprachen.126127$X$ heißt stärker typisiert als $Y$, wenn $X$ mehr bzw. nützlichere Typen hat als $Y$.128\end{definition}129130\begin{beispiel}[Typisierungsstärke]131Die stärke der Typisierung ist abhängig von dem Anwendungszenario. So hat C im132Gegensatz zu Python, Java oder Haskell beispielsweise keine booleschen Datentypen.133134Im Gegensatz zu Haskell hat Java keine GADTs\footnote{generalized algebraic data type}.135\end{beispiel}136137\begin{definition}[Polymorphie]\xindex{Polymorphie}%138\begin{defenum}139\item Ein Typ heißt polymorph, wenn er mindestens einen Parameter hat.140\item Eine Funktion heißt polymorph, wenn ihr Verhalten nicht von dem141konkreten Typen der Parameter abhängt.142\end{defenum}143\end{definition}144145\begin{beispiel}[Polymorphie]146In Java sind beispielsweise Listen polymorphe Typen:147148\inputminted[numbersep=5pt, tabsize=4]{java}{scripts/java/list-example.java}149150Entsprechend sind auf Listen polymorphe Operationen wie \texttt{add} und151\texttt{remove} definiert.152\end{beispiel}153154\begin{definition}[Statische und dynamische Typisierung]\xindex{Typisierung!statische}\xindex{Typisierung!dynamische}%155\begin{defenum}156\item Eine Programmiersprache heißt \textbf{statisch typisiert}, wenn eine157Variable niemals ihren Typ ändern kann.158\item Eine Programmiersprache heißt \textbf{dynamisch typisiert}, wenn eine159Variable ihren Typ ändern kann.160\end{defenum}161\end{definition}162163Beispiele für statisch typisierte Sprachen sind C, Haskell und Java.164Beispiele für dynamisch typisierte Sprachen sind Python und PHP.165\goodbreak166Vorteile statischer Typisierung sind:167168\begin{itemize}169\item \textbf{Performance}: Der Compiler kann mehr Optimierungen vornehmen.170\item \textbf{Syntaxcheck}: Da der Compiler die Typen zur Compile-Zeit überprüft,171gibt es in statisch typisierten Sprachen zur172Laufzeit keine Typfehler.173\end{itemize}174175Vorteile dynamischer Typisierung sind:176177\begin{itemize}178\item Manche Ausdrücke, wie der Y-Combinator in Haskell, lassen sich nicht179typisieren.180\end{itemize}181182Der Gedanke bei dynamischer Typisierung ist, dass Variablen keine Typen haben.183Nur Werte haben Typen. Man stellt sich also Variablen eher als Beschriftungen für184Werte vor. Bei statisch typisierten Sprachen stellt man sich hingegen Variablen185als Container vor.186187\begin{definition}[Explizite und implizite Typisierung]\xindex{Typisierung!explizite}\xindex{Typisierung!implizite}%188Sei $X$ eine Programmiersprache.189\begin{defenum}190\item $X$ heißt \textbf{explizit typisiert}, wenn für jede191Variable der Typ explizit genannt wird.192\item $X$ heißt \textbf{implizit typisiert}, wenn der Typ einer193Variable aus den verwendeten Operationen abgeleitet werden kann.194\end{defenum}195\end{definition}196197Sprachen, die implizit typisieren können nutzen dazu Typinferenz.198199Beispiele für explizit typisierte Sprachen sind C, C++ und Java.200Beispiele für implizit typisierte Sprachen sind JavaScript, Python, PHP und Haskell.201202Mir ist kein Beispiel einer Sprache bekannt, die dynamisch und explizit typisiert203ist.204205Vorteile expliziter Typisierung sind:206207\begin{itemize}208\item \textbf{Lesbarkeit}209\end{itemize}210211Vorteile impliziter Typisierung sind:212213\begin{itemize}214\item \textbf{Tippfreundlicher}: Es ist schneller zu schreiben.215\item \textbf{Anfängerfreundlicher}: Man muss sich bei einfachen Problemen216keine Gedanken um den Typ machen.217\end{itemize}218219\begin{definition}[Duck-Typing und strukturelle Typisierung]\xindex{Duck-Typing}\xindex{Typisierung!strukturelle}%220\begin{defenum}221\item Eine Programmiersprache verwendet \textbf{Duck-Typing}, wenn die Parameter einer222Methode nicht durch die explizite Angabe von Typen festgelegt werden, sondern223durch die Art wie die Parameter verwendet werden.224\item Eine Programmiersprache verwendet \textbf{strukturelle Typisierung}, wenn die225Parameter einer Methode nicht durch die explizite Angabe von Typen226festgelegt werden, sondern explizit durch die Angabe von Methoden.227\end{defenum}228\end{definition}229230Strukturelle Typsierung wird auch \textit{typsicheres Duck-Typing} genannt.231Der Satz, den man im Zusammenhang mit Duck-Typing immer höhrt, ist232233\enquote{When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.}234235\begin{beispiel}[Strukturelle Typisierung]236Folgende Scala-Methode erwartet ein Objekt, das eine Methode namens \texttt{quack}237besitzt:238239\inputminted[numbersep=5pt, tabsize=4]{scala}{scripts/scala/duck-typing-example.scala}240241Diese Funktion ist vom Typ \texttt{(duck: AnyRef{def quack(value: String): String})Unit}.242\end{beispiel}243244\section{Kompilierte und interpretierte Sprachen}245Sprachen werden überlicherweise entweder interpretiert oder kompiliert,246obwohl es Programmiersprachen gibt, die beides unterstützen.247248C und Java werden kompiliert, Python und TCL interpretiert.249250\section{Dies und das}251\begin{definition}[Seiteneffekt]\xindex{Seiteneffekt}\index{Nebeneffekt|see{Seiteneffekt}}\index{Wirkung|see{Seiteneffekt}}%252Seiteneffekte sind Veränderungen des Zustandes eines Programms.253\end{definition}254255Manchmal werden Seiteneffekte auch als Nebeneffekt oder Wirkung bezeichnet.256Meistens meint man insbesondere unerwünschte oder überaschende Zustandsänderungen.257258\begin{definition}[Unifikation]\xindex{Unifikation}%259Die Unifikation ist eine Operation in der Logik und dient zur Vereinfachung260prädikatenlogischer Ausdrücke.261Der Unifikator ist also eine Abbildung, die in einem Schritt dafür sorgt, dass262auf beiden Seiten der Gleichung das selbe steht.263\end{definition}264265\begin{beispiel}[Unifikation\footnotemark]266Gegeben seien die Ausdrücke267\begin{align*}268A_1 &= \left(X, Y, f(b) \right)\\269A_2 &= \left(a, b, Z \right)270\end{align*}271Großbuchstaben stehen dabei für Variablen und Kleinbuchstaben für atomare272Ausdrücke.273274Ersetzt man in $A_1$ nun $X$ durch $a$, $Y$ durch $b$ und in $A_2$275die Variable $Z$ durch $f\left(b\right)$, so sind sie gleich oder276\enquote{unifiziert}. Man erhält277278\begin{align*}279\sigma(A_1) &= \left(a, b, f(b) \right)\\280\sigma(A_2) &= \left(a, b, f(b) \right)281\end{align*}282283mit284\[\sigma = \{X \mapsto a, Y \mapsto b, Z \mapsto f(b)\}\]285\end{beispiel}286287\begin{definition}[Allgemeinster Unifikator]\xindex{Unifikator!allgemeinster}%288Ein Unifikator $\sigma$ heißt \textit{allgemeinster Unifikator}, wenn289es für jeden Unifikator $\gamma$ eine Substitution $\delta$ mit290\[\gamma = \delta \circ \sigma\]291gibt.292\end{definition}293294\begin{beispiel}[Allgemeinster Unifikator\footnotemark]295Sei296\[C = \Set{f(a,D) = Y, X = g(b), g(Z) = X}\]297eine Menge von Gleichungen über Terme.298299Dann ist300\[\gamma = [Y \text{\pointer} f(a,b), D \text{\pointer} b, X \text{\pointer} g(b), Z \text{\pointer} b]\]301ein Unifikator für $C$. Jedoch ist302\[\sigma = [Y \text{\pointer} f(a,D), X \text{\pointer} g(b), Z \text{\pointer} b]\]303der allgemeinste Unifikator. Mit304\[\delta = [D \text{\pointer} b]\]305gilt $\gamma = \delta \circ \sigma$.306\end{beispiel}307\footnotetext{Folie 268 von Prof. Snelting}308309\begin{algorithm}[h]310\begin{algorithmic}311\Function{unify}{Gleichungsmenge $C$}312\If{$C == \emptyset$}313\State \Return $[]$314\Else315\State Es sei $\Set{\theta_l = \theta_r} \cup C' == C$316317\If{$\theta_l == \theta_r$}318\State \Call{unify}{$C'$}319\ElsIf{$\theta_l == Y$ and $Y \notin FV(\theta_r)$}320\State \Call{unify}{$[Y \text{\pointer} \theta_r] C'$} $\circ [Y \text{\pointer} \theta_r]$321\ElsIf{$\theta_r == Y$ and $Y \notin FV(\theta_l)$}322\State \Call{unify}{$[Y \text{\pointer} \theta_l] C'$} $\circ [Y \text{\pointer} \theta_l]$323\ElsIf{$\theta_l == f(\theta_l^1, \dots, \theta_l^n)$ and $\theta_r == f(\theta_r^1, \dots, \theta_r^n$}324\State \Call{unify}{$C' \cup \Set{\theta_l^1 = \theta_r^1, \dots \theta_l^n = \theta_r^n}$}325\Else326\State fail327\EndIf328\EndIf329\EndFunction330\end{algorithmic}331\caption{Klassischer Unifikationsalgorithmus}332\label{alg:klassischer-unifikationsalgorithmus}333\end{algorithm}334335Dieser klassische Algorithmus hat eine Laufzeit von $\mathcal{O}(2^n)$ für folgendes336Beispiel:337338\[f(X_1, X_2, \dots, X_n) = f(g(X_0, X_0), g(X_1, X_1), \dots, g(X_{n-1}, X_{n-1}) )\]339340Der \textit{Paterson-Wegman-Unifikationsalgorithmus}\xindex{Paterson-Wegman-Unifikationsalgorithmus} ist deutlich effizienter.341Er basiert auf dem \textit{Union-Find-Algorithmus}\xindex{Union-Find-Algorithmus}342und funktioniert wie folgt:343344\begin{algorithm}[h]345\begin{algorithmic}346\Function{unify}{Knoten $p$, Knoten $q$}347\State $s \gets \Call{find}{p}$348\State $t \gets \Call{find}{q}$349\If{$s == t$ oder $s.\Call{getAtom}{} == t.\Call{getAtom}{}$}350\State \Return \texttt{True}351\EndIf352353\If{$s,t$ Knoten für gleichen Funktor, mit Nachfolgern $s_1, \dots , s_n$ bzw. $t_1 , \dots , t_n$ }354\State $\Call{union}{s,t}$355\State $k \gets 1$356\State $b \gets $ \texttt{True}357\While{$k \leq n$ and $b$}358\State $b \gets \Call{unify}{s_k, t_k}$359\State $k \gets k + 1$360\EndWhile361\State \Return \texttt{True}362\EndIf363364\If{$s$ oder $t$ ist Variablen-Knoten}365\State $\Call{union}{s,t}$366\State \Return \texttt{True}367\EndIf368369\State \Return \texttt{False}370\EndFunction371\end{algorithmic}372\caption{Paterson-Wegeman Unifikationsalgorithmus}373\label{alg:paterson-wegeman-unifikationsalgorithmus}374\end{algorithm}375376377\footnotetext{\url{https://de.wikipedia.org/w/index.php?title=Unifikation\_(Logik)&oldid=116848554\#Beispiel}}378379