[codestyle] renamed programmingstyle/ to codestyle/

This commit is contained in:
2019-12-06 23:30:21 +01:00
parent 93f18143f1
commit f24c14e6f5
23 changed files with 22 additions and 23 deletions

View File

@@ -0,0 +1,32 @@
function sines = calculateSines(x, amplitudes, frequencies)
% sines = calculateSines(x, amplitudes, frequencies)
%
% Function calculates sinewaves with all combinations of
% given amplitudes and frequencies.
% Arguments: x, a vector of radiants for which the sine should be
% computed.
% amplitudes, a vector of amplitudes.
% frequencies, a vector of frequencies.
%
% Returns: a 3-D Matrix of sinewaves, 2nd dimension represents
% the amplitudes, 3rd the frequencies.
sines = zeros(length(x), length(amplitudes), length(frequencies));
for i = 1:length(amplitudes)
sines(:,i,:) = sinesWithFrequencies(x, amplitudes(i), frequencies);
end
end
function sines = sinesWithFrequencies(x, amplitude, frequencies)
sines = zeros(length(x), length(frequencies));
for i = 1:length(frequencies)
sines(:,i) = sinewave(x, amplitude, frequencies(i));
end
end
function sine = sinewave(x, amplitude, frequency)
sine = sin(2 .* pi .* x *frequency) .* amplitude;
end

View File

@@ -0,0 +1,12 @@
BASENAME=codestyle
all : pdf
include ../../chapter.mk
# script:
pdf : chapter
clean : cleanchapter
cleanall : cleanallchapter

View File

@@ -0,0 +1,61 @@
% Copyright 2007 by Till Tantau
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/licenses/LICENSE for more details.
\usepackage{color}
\definecolor{karminrot}{RGB}{165,30,55}
\definecolor{gold}{RGB}{180,160,105}
\definecolor{anthrazit}{RGB}{50 ,65 ,75 }
\mode<presentation>
\setbeamercolor*{normal text}{fg=anthrazit,bg=white}
\setbeamercolor*{alerted text}{fg=anthrazit}
\setbeamercolor*{example text}{fg=anthrazit}
\setbeamercolor*{structure}{fg=gold,bg=karminrot}
\providecommand*{\beamer@bftext@only}{%
\relax
\ifmmode
\expandafter\beamer@bftext@warning
\else
\expandafter\bfseries
\fi
}
\providecommand*{\beamer@bftext@warning}{%
\ClassWarning{beamer}
{Cannot use bold for alerted text in math mode}%
}
\setbeamerfont{alerted text}{series=\beamer@bftext@only}
\setbeamercolor{palette primary}{fg=karminrot,bg=white}
\setbeamercolor{palette secondary}{fg=gold,bg=white}
\setbeamercolor{palette tertiary}{fg=anthrazit,bg=white}
\setbeamercolor{palette quaternary}{fg=black,bg=white}
\setbeamercolor{sidebar}{bg=karminrot!100}
\setbeamercolor{palette sidebar primary}{fg=karminrot}
\setbeamercolor{palette sidebar secondary}{fg=karminrot}
\setbeamercolor{palette sidebar tertiary}{fg=karminrot}
\setbeamercolor{palette sidebar quaternary}{fg=karminrot}
\setbeamercolor{item projected}{fg=black,bg=black!20}
\setbeamercolor*{block body}{}
\setbeamercolor*{block body alerted}{}
\setbeamercolor*{block body example}{}
\setbeamercolor*{block title}{parent=structure}
\setbeamercolor*{block title alerted}{parent=alerted text}
\setbeamercolor*{block title example}{parent=example text}
\setbeamercolor*{titlelike}{parent=structure}
\mode
<all>

View File

@@ -0,0 +1,20 @@
\documentclass[12pt]{book}
\input{../../header}
\lstset{inputpath=../code}
\graphicspath{{figures/}}
\typein[\pagenumber]{Number of first page}
\typein[\chapternumber]{Chapter number}
\setcounter{page}{\pagenumber}
\setcounter{chapter}{\chapternumber}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\include{codestyle}
\end{document}

View File

@@ -0,0 +1,429 @@
\chapter{\tr{Code style}{Programmierstil}}
\shortquote{Any code of your own that you haven't looked at for six or
more months might as well have been written by someone
else.}{Eagleson's law}
Cultivating a good code style is not just a matter of good taste but
rather is a key ingredient for readability and maintainability of code
and, in the end, facilitates reproducibility of scientific
results. Programs should be written and structured in a way that
supports outsiders as well the author himself --- a few weeks or
months after it was written --- to understand the programs'
rationale. Clean code pays off for the original author as well as
others that are supposed to use the code.
Clean code addresses several issues:
\begin{enumerate}
\item The programs' structure.
\item Naming of scripts and functions.
\item Naming of variables and constants.
\item Application of indentation and empty lines to define blocks.
\item Use of comments and inline documentation.
\item Delegation of repeated code to functions and dedicated
subroutines.
\end{enumerate}
\section{Organization of programs on the file system}
While introducing scripts and functions we suggested a typical program
layout (box\,\ref{whenscriptsbox}). The idea is to create a single
entry point by having one script that controls the rest of the program
by calling functions that work on the data and managing the
results. Applying this structure makes it easy to understand the flow
of the program but two questions remain: (i) How to organize the files
on the file system and (ii) how to name them that the controlling
script is easily identified among the other \codeterm{m-files}.
Upon installation \matlab{} creates a folder called \file{MATLAB} in
the user space (Windows: My files, Linux: Documents, MacOS:
Documents). Since this folder is already appended to the Matlab search
path (Box~\ref{matlabpathbox}), it is easiest to stick to it for the
moment. Of course, any other location can specified as well. Generally
it is of great advantage to store related scripts and functions within
the same folder on the hard drive. An easy approach is to create a
project-specific folder structure that contains sub-folders for each
task (analysis) and to store all related \codeterm{m-files}
(screenshot \ref{fileorganizationfig}). In these task-related folders
one may consider to create a further sub-folder to store results
(created figures, result data). On the project level a single script
(\file{analysis.m}) controls the whole process. In parallel to the
project folder we suggest to create an additional folder for functions
that are or may be relevant across different projects.
Within such a structure it is quite likely that programs in different
projects share the same name (e.g. a \varcode{load\_data.m}
function). Usually this will not lead to conflicts due to the way
matlab searches for matching functions which always starts in the
current folder (more information on the \matlab-path in
Box~\ref{matlabpathbox}).
\begin{figure}[tp]
\includegraphics[width=0.75\textwidth]{program_organization}
\titlecaption{\label{fileorganizationfig} Possible folder structure
for maintaining program code on the file system.}{For each project
one maintains an individual folder in which analyses or tasks may
be structured in sub-folders. Within each analysis a \file{main.m}
script is the entry point for the analyses. On the project level
there could be a single script that triggers and controls all
analyses and tasks in the sub-folders. Functions that are of
general interest across projects are best kept in a dedicated
folder outside the project sub-structure.}
\end{figure}
\begin{ibox}[tp]{\label{matlabpathbox}\matlab{} search path}
The \codeterm{search path} defines where \matlab{} looks for scripts
and functions. When calling a function from the command line
\matlab{} needs to figure out which function is addressed and starts
looking for it in the current path. If this fails it will crawl all
locations listed in the search path (see figure). The
\codeterm{search path} is basically a list of folders. \matlab{}
will go through this list from front to end and the search will stop
on the first match. This implies that the order in the search path
may affect which version of functions that share the same name is
used. Note: \matlab{} does not perform a recursive search. That is,
a function that resides in a sub-folder that is not explicitly
listed in the \codeterm{search path} will not be found.
\vspace{2ex}
\includegraphics[width=0.9\textwidth]{search_path}
\vspace{1.5ex}
The search path can be managed from the command line by using the
functions \code{addpath()} or \code{userpath()}. Alternatively, the
\matlab{} UI offers a graphical tool for adding/removing paths, or
changing the order of entries.
The current working directory can be changed via the UI or also the
command line using the command \code{cd} (for change directory). The
current path is shown in the current directory text field of the UI
or can be requested using the command \code{pwd} (for present work
directory). The function \code{which()} shows the full path of the
actually used function. For example, finding out which \code{mean()}
function is used gives a result similar to:
\begin{lstlisting}[label=useofwhich, caption={Use of 'which'}]
>> which('mean')
/Applications/MATLAB2018b.app/toolbox/matlab/datafun/mean.m
\end{lstlisting}
\end{ibox}
\section{Naming things}
The dictum of good code style is: ``Program code must be readable.''
Expressive names are extraordinarily important in this respect. Even
if it is tricky to find expressive names that are not overly long,
naming should be taken seriously.
\matlab{} has a few rules about names: Names must not start with a
number, they must not contain blanks or other special characters like
e.g. German Umlauts. Otherwise one is free to use whatever suits. The
names of pre-defined functions shipped with \matlab{} follows several
patterns:
\begin{itemize}
\item Names are always lowercase.
\item Names are often abbreviations (e.g. \code{xcorr()}
stands for cross-correlation \code{repmat()} for ``repeat matrix'').
\item Functions that convert between formats are named according to
the pattern ``format2format'' (e.g. \code{num2str()} for ``number to string'' conversion).
\end{itemize}
There are other common patterns such as the \emph{camelCase} in which
the first character of compound words is capitalized. Other
conventions use the underscore to separate the individual words
(\emph{snake\_case}). A function that counts the number of action
potentials could be named \file{spikeCount.m} or
\file{spike\_count.m}.
The same naming rules apply for scripts and functions as well as
variables and constants.
\subsection{Naming scripts and functions}
\matlab{} will search the search path (Box \ref{matlabpathbox})
exclusively by name. This search is case-sensitive which implies that
the files \file{test\_function.m} and \file{Test\_function.m} are two
different things. It is self-evident that choosing such names is
nonsensical because the tiny difference in the name contains no cue
about the difference between the two versions and the function names
themselves tell close to nothing about the purpose. Finding good names
is not trivial. Sometimes it is harder than the programming
itself. Choosing \emph{expressive names} that provide information about a
function's purpose, however, pays off!
\begin{important}[Naming scripts and functions]
Names of functions and scripts should be expressive in the sense
that the name provides information about the function's purpose.
(\file{estimate\_firingrate.m} tells much more than
\file{exercise1.m}). Choosing a good name replaces large parts of
the documentation.
\end{important}
\subsection{Naming variables and constants}
While the names of scripts and functions describe the purpose, names
of variables describe the stored content. A variable storing the
average number of actions potentials could be called\\
\varcode{average\_spike\_count}. If this variable is meant to store
multiple spike counts the plural form would be appropriate\\
(\varcode{average\_spike\_counts}).
The control variables used in the head of a \code{for} loop are often
simply named \varcode{i}, \varcode{j} or \varcode{k}. This kind-of
clashes with the previously made statements but since it is a very
common pattern the meaning of such variables in the context of the
loop is quite obvious. This should, however, be the only exception to
the general rule of expressive naming.
\begin{important}[Naming of variables]
The names of variables should be expressive. That is, the name
itself should tell about the content of the variable. The name
\varcode{spike\_count} tells much more about the stored information
than \varcode{x}. Choosing a good variable name replaces additional
comments.
\end{important}
\section{Code style}
Readability of program code depends strongly on whether or not a
consistent code style is applied. A program that is only randomly
indented or that contains lots of empty lines is very hard to read and
to comprehend. Even though the \matlab{} language (as many others)
does not enforce indentation, indentation is very powerful for
defining coherent blocks. The \matlab{} editor supports this by an
auto-indentation mechanism. A selected section of the code and be
automatically indented by pressing \keycode{Ctrl-I}.
Interspersing empty lines is very helpful to separate regions in the
code that belong together. Too many empty lines, however lead to
hard-to-read code because it might require more space than a granted
by the screen and thus takes overview.
The following two listings show basically the same implementation of a
random walk\footnote{A random walk is a simple simulation of Brownian
motion. In each simulation step an agent takes a step into a
randomly chosen direction.} once in a rather chaotic version
(listing \ref{chaoticcode}) then in cleaner way (listing
\ref{cleancode})
\begin{lstlisting}[label=chaoticcode, caption={Chaotic implementation of the random-walk.}]
num_runs = 10; max_steps = 1000;
positions = zeros(max_steps, num_runs);
for run = 1:num_runs
for step = 2:max_steps
x = randn(1);
if x<0
positions(step, run)= positions(step-1, run)+1;
elseif x>0
positions(step,run)=positions(step-1,run)-1;
end
end
end
\end{lstlisting}
\pagebreak[4]
\begin{lstlisting}[label=cleancode, caption={Clean implementation of the random-walk.}]
num_runs = 10;
max_steps = 1000;
positions = zeros(max_steps, num_runs);
for run = 1:num_runs
for step = 2:max_steps
x = randn(1);
if x < 0
positions(step, run) = positions(step-1, run) + 1;
elseif x > 0
positions(step, run) = positions(step-1, run) - 1;
end
end
end
\end{lstlisting}
\section{Using comments}
It is common to provide extra information about the meaning of program
code by adding comments. In \matlab{} comments are indicated by the
percent character \code{\%}. Anything that follows the percent
character in a line is ignored and considered a comment. When used
sparsely comments can be immensely helpful. Comments
are short sentences that describe the meaning of the (following) lines
in the program code. During the initial implementation of a function
they can be used to guide the development but have the tendency to
blow up the code and decrease readability. By choosing expressive
variable and function names, most lines should be self-explanatory.
For example stating the obvious does not really help and should be
avoided:\\ \varcode{ x = x + 2; \% add two to x}\\
\begin{important}[Using comments]
\begin{itemize}
\item Comments describe the rationale of the respective code block.
\item Comments are good and helpful --- they must be true, however!
\item A wrong comment is worse than a non-existent one!
\item Comments must be maintained just as the code. Otherwise they
may become wrong and worse than meaningless!
\end{itemize}
\widequote{Good code is its own best documentation. As you're about to add
a comment, ask yourself, ``How can I improve the code so that this
comment isn't needed?'' Improve the code and then document it to
make it even clearer.}{Steve McConnell}
\end{important}
\pagebreak[4]
\section{Documenting functions}
All pre-defined \matlab{} functions begin with a comment block that
describes the purpose of the function, the required and optional
arguments, and the values returned by the function. Using the
\code{help} command one can display these comments and learn how to
use the function properly. Self-written functions can and should be
documented in a similar way. Listing ~\ref{localfunctions} shows a
well documented function.
\begin{important}[Documenting functions]
Functions must be properly documented, otherwise a user (the author
him- or herself) must read and understand the function code which is
a waste of time!
\begin{itemize}
\item Describe with a few sentences the purpose of the function.
\item Note the function head to illustrate the order of the argments.
\item For each argument state the purpose, the expected data type
(number, vector, matrix, etc.) and, if applicable, the unit in
which a provided number must be given (e.g. seconds if a time is
expected).
\item The same for all return values.
\end{itemize}
\end{important}
\section{Delegating tasks in functions}
Comments and empty lines are used to organize code into logical blocks
and to briefly explain what they do. Whenever one feels tempted to do
this, one could also consider to delegate the respective task to a
function. In most cases this is preferable.
Not delegating the tasks leads to very long \codeterm{m-files} which
can be confusing. Sometimes such a code is called ``spaghetti
code''. It is high time to think about delegation of tasks to
functions.
\begin{important}[Delegating to functions]
When should one consider delegating tasks to specific functions?
\begin{itemize}
\item Whenever one needs more than two indentation levels to
organize to code.
\item Whenever the same lines of code are repeated more than once.
\item Whenever one is tempted to use copy-and-paste.
\end{itemize}
\end{important}
\subsection{Local and nested functions}
Generally, functions live in their own \codeterm{m-files} that have
the same name as the function itself. Delegating tasks to functions
thus leads to a large set of \codeterm{m-files} which increases
complexity and may lead to confusion. If the delegated functionality
is used in multiple instances, it is still advisable to do so. On the
other hand, when the delegated functionality is only used within the
context of another function \matlab{} allows to define
\codeterm[function!local]{local functions} and
\codeterm[function!nested]{nested functions} within the same
file. Listing \ref{localfunctions} shows an example of a local
function definition.
\pagebreak[3] \lstinputlisting[label=localfunctions, caption={Example
for local functions.}]{calculateSines.m}
\emph{Local function} live in the same \codeterm{m-file} as the main
function and are only available in this context. Each local function
has its own \codeterm{scope}, that is, the local function can not
access (read or write) variables of the calling function. Interaction
with the local function requires to pass all required arguments and to
take care of the return values of the function.
\emph{Nested functions} are different in this respect. They are
defined within the body of the parent function (between the keywords
\code{function} and \code{end}) and have full access to all variables
defined in the parent function. Working (in particular changing) the
parent's variables is handy on the one side, but is also risky. One
should take care when defining nested functions.
\section{Specifics when using scripts}
A similar problem as with nested function arises when using scripts
(instead of functions). All variables that are defined within a script
become available in the global \codeterm{workspace}. There is the risk
of name conflicts, that is, a called sub-script redefines or uses the
same variable name and may \emph{silently} change its content. The
user will not be notified about this change and the calling script may
expect a completely different content. Bugs that are based on such
mistakes are hard to find since the program itself looks perfectly
fine.
To avoid such issues one should design scripts in a way that they
perform their tasks independent from other scripts and functions.
A common use case for a script could be to control the analyses made
on many datasets and to collect the results. A good script is still
not too long and is thus easy to comprehend. Another advantage of
small task-related scripts is that they can be directly executed by
either calling them from the command line or pressing \keycode{F5} in
the editor. Should it fail there will be a proper error message that
provides important information to track and fix the bug.
\begin{important}[Structuring scripts]
\begin{itemize}
\item Similar to functions script should solve one task and should
not be too long.
\item Scripts should work independently of existing variables in the
global workspace.
\item Often it is advisable to start a script with deleting
variables (\code{clear}) from the workspace and most of the times
it is also good to close all open figures (\code{close all}). Be
careful if a the respective script has been called by another one.
\item Clean up the workspace at the end of a script. Delete
(\code{clear}) all variables that are no longer needed.
\item Consider to write functions instead of scripts.
\end{itemize}
\end{important}
\section{Summary}
Program code must be readable. Names of variables, functions and
scripts should be expressive and describe their purpose (scripts and
functions) or their content (variables). Cultivating a personalized
code style is perfectly fine as long as it is consistent. Many
programming languages or communities have their own traditions. It is
advisable to adhere to these.
Repeated tasks should (to be read as must) be delegated to
functions. In cases in which a function is only locally applied and
not of more global interest across projects consider to define it as
\codeterm[function!local]{local function} or
\codeterm[function!nested]{nested function}. Taking care to increase
readability and comprehensibility pays off, even to the author!
\footnote{Reading tip: Robert C. Martin: \textit{Clean Code: A Handbook of
Agile Software Craftmanship}, Prentice Hall}
\shortquote{Programs must be written for people to read, and only
incidentally for machines to execute.}{Abelson / Sussman}
\shortquote{Any fool can write code that a computer can
understand. Good programmers write code that humans can
understand.}{Martin Fowler}
\shortquote{First, solve the problem. Then, write the code.}{John
Johnson}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%\printsolutions

View File

@@ -0,0 +1,446 @@
\chapter{\tr{Programming style}{Programmierstil}}
\shortquote{Any code of your own that you haven't looked at for six or
more months might as well have been written by someone
else.}{Eagleson's law}
\selectlanguage{ngerman}
Guter Programmierstil ist keine Frage des guten Geschmacks sondern des
Verst\"andnisses von Programmcode und ein Baustein in dem Bestreben
wissenschaftlichen Erkenntnisgewinn reproduzierbar zu
machen.
Programme sollten so geschrieben und strukturiert sein, dass es sowohl
einem Au{\ss}enstehenden als auch einem selbst --- nach ein paar
Wochen oder Monaten! --- leicht f\"allt den Programmablauf
nachzuvollziehen und zu verstehen. Saubere Programmierung zahlt sich
in erster Linie f\"ur einen selbst aus und macht es aber gleichzeitig
f\"ur andere Personen leichter, den Code nachzuvollziehen und zu
benutzen.
Guter Programmierstil greift auf unterschiedlichen Ebenen an:
\begin{enumerate}
\item Die Dateistruktur von Programmen.
\item Die Namensgebung von Skripten und Funktionen.
\item Die Namensgebung f\"ur Variablen und Konstanten.
\item Die Verwendung von Einr\"uckungen und Leerzeilen um Bl\"ocke im
Code hervorzuheben.
\item Verwendung von Kommentaren und Hilfetexten.
\item Auslagerung von Funktionalit\"at in eigene Funktionen.
\end{enumerate}
\section{Organisation von Programmdateien im Dateisystem}
In der Einf\"uhrung zu Funktionen und Skripten wurde schon einmal ein
typisches Programmlayout vorgestellt (box\,\ref{whenscriptsbox}). Hier
wurde vorgeschlagen ein Skript als Kontrollskript zu verwenden. Dieses
kontrolliert den weiteren Programmablauf, ruft Funktionen auf,
\"ubergibt Argumente und nimmt R\"uckgabewerte entgegen. Eine solche
Struktur macht es einfach den Ablauf zu verstehen. Es bleibt aber die
Frage, wie man das Kontrollskript unter den anderen \codeterm{m-files}
als solches erkennt. Um dieses zu erleichtern gilt es zwei Dinge zu
beachten: (i) Wie werden Programme im Dateisystem organisiert? (ii) Wie
werden Programme benannt?
Es hilft ungemein, wenn zusammengeh\"orige Skripte und Funktionen im
gleichen Ordner auf der Festplatte zu finden sind. Es bietet sich also
an, f\"ur jede Analyse einen eigenen Ordner anzulegen und in diesem
die zugeh\"origen \codeterm{m-files} abzulegen. Auf eine tiefere
Schachtelung in weitere Unterordner kann in der Regel verzichtet
werden. \matlab{} erzeugt einen ``MATLAB'' Ordner im eigenen
\file{Documents} (Linux) oder \file{Eigene Dokumente} (Windows)
Ordner. Es bietet sich an, diesen Ordner als Wurzelverzeichnis f\"ur
eigene Arbeiten zu verwenden. Nat\"urlich kann auch jeder andere Ort
gew\"ahlt werden. In dem Beispiel in \figref{fileorganizationfig} wird
innerhalb dieses Ordners f\"ur jedes Projekt ein eigener Unterordner
erstellt, in welchem wiederum f\"ur jedes Problem, jede Analyse ein
weiterer Unterodner erstellt wird. In diesen liegen sowohl die
ben\"otigten \codeterm{m-files} also auch die Resultate der Analyse
(Abbildungen, Daten-Dateien). Zu bemerken sind noch zwei weitere
Dinge. Im Projektordner existiert ein Skript (analysis.m), das dazu
gedacht ist, alle Analysen aufzurufen. Des Weiteren gibt es parallel
zu den Projektordnern einen \file{functions}-Ordner in dem Funktionen
liegen, die in mehr als einem Projekt oder einer Analyse gebraucht
werden.
Beim Betrachten dieses Layouts f\"allt auf, dass es sehr
wahrscheinlich ist, dass bestimmte Namen f\"ur Funktionen und Skripte
mehrfach verwendet werden. Es ist nicht verwunderlich, wenn eine
\file{load\_data.m} Funktion in jeder Analyse vorkommt. In der Regel
wird dies nicht zu Konflikten f\"uhren, da \matlab{} zuerst im
aktuellen Ordner nach passenden Dateien sucht (mehr Information zum
\matlab-Suchpfad in Box~\ref{matlabpathbox}).
\begin{figure}[tp]
\includegraphics[width=0.75\textwidth]{program_organization}
\titlecaption{\label{fileorganizationfig} M\"ogliche Organisation von
Programmcode im Dateisystem.}{ F\"ur jedes Projekt werden
Unterordner f\"ur die einzelnen Analysen angelegt. Auf Ebene des
Projektes k\"onnte es ein Skript (hier ``analysis.m'') geben,
welches alle Analysen in den Unterordnern anst\"o{\ss}t.}
\end{figure}
\begin{ibox}[tp]{\label{matlabpathbox}Der \matlab{} Suchpfad}
Der Suchpfad definiert, wo \matlab{} nach Skripten und Funktionen
sucht. Wird eine Funktion aufgerufen wird zun\"achst im aktuellen
Arbeitsverzeichnis einem Treffer gesucht. Schl\"agt diese Suche
fehl, so arbeitet sich \matlab{} durch den \codeterm{Suchpfad}
(siehe Abbildung). Der \codeterm{Suchpfad} ist eine Liste von
Ordnern in denen \matlab{} nach Funktionen und Skripten suchen
soll. Die Suche nach der aufgerufenen Funktion wird dabei von oben
nach unten durchgef\"uhrt. Das heisst, dass es bei
Namensgleichheit eine Rolle spielen kann an welcher Stelle im
Suchpfad der erste Treffer gefunden wird. Wichtig: \matlab{} sucht
nicht rekursiv! Wenn die gew\"unschte Funktion in einem Unterordner
des aktuellen Arbeitsverzeichnisses liegt, dieses aber nicht
explizit im Suchpfad enthalten ist, so wird die Funktion nicht
gefunden.
\vspace{2ex}
\includegraphics[width=0.9\textwidth]{search_path}
\vspace{1.5ex}
Der Suchpfad kann sowohl \"uber die Kommandozeile mit dem Kommandos
\code{addpath()} und \code{userpath()} als auch\"uber die in der
Abbildung gezeigte GUI angezeigt und eingestellt werden. Die GUI
erlaubt Ordner aus dem Suchpfad zu entfernen, neue Ordner (optional
inklusive aller Unterordner) hinzuzuf\"ugen oder die Reihenfolge der
Pfade zu ver\"andern.
Zum Wechseln des aktuellen Arbeitsverzeichnisses wird das Kommando
\code{cd} verwendet. \code{which} zeigt an, in welchem Pfad eine
bestimmte Funktion gefunden wurde. Das aktuelle Areitsverzeichnis
wird durch den Aufruf \code{pwd} auf der Kommandozeile ausgegeben.
\end{ibox}
\section{Namensgebung von Funktionen und Skripten}
\matlab{} sucht Funktionen und Skripte ausschlie{\ss}lich anhand des
Namens. Dabei spielt die Gro{\ss}- und Kleinschreibung eine Rolle. Die
beiden Dateien \file{test\_funktion.m} und \file{Test\_Funktion.m}
zwei unterschiedliche Funktionen benennen k\"onnen. Diese Art der
Variation des Namens ist nat\"urlich nicht sinnvoll. Sie tr\"agt keine
Information \"uber den Unterschied der beiden Funktionen. Auch sagt
der Name nahezu nichts \"uber den Zweck der Funktion aus.
Die Namensgebung f\"allt mitunter nicht leicht --- manchmal ist es
sogar der schwierigste Aspekt des Programmierens! Ausdrucksstarke
Namen zu finden lohnt sich aber. Ausdrucksstark bedeutet, dass sich
aus dem Namen R\"uckschl\"usse auf den Zweck ziehen lassen sollte.
\begin{important}[Benennung von Funktionen und Skripten]
Die Namen von Funktionen und Skripten sollten m\"oglichst viel \"uber
die Funktionsweise oder den Zweck aussagen (\file{firingrates.m}
statt \file{uebung.m}). Gute Namen f\"ur Funktionen und Skripte sind
die beste Dokumentation.
\end{important}
In Namen verbietet \matlab{} verbietet Leerzeichen, Sonderzeichen und
Umlaute. Namen d\"urfen auch nicht mit Zahlen anfangen. Es mach f\"ur
die Namensgebung selbst keine weiteren Vorgaben. Allerdings folgt die
Benennung der in \matlab{} vordefinierten Funktionen gewissen Mustern:
\begin{itemize}
\item Namen werden immer klein geschrieben.
\item Es werden gerne Abk\"urzungen eingesetzt (z.B. \code{xcorr()}
f\"ur die Kreuzkorrelation oder \code{repmat()} f\"ur ``repeat matrix'')
\item Funktionen, die zwischen Formaten konvertieren sind immer nach
dem Muster ``format2format'' (z.B. \code{num2str()} f\"ur die
Konvertierung ``number to string'', Umwandlung eines numerischen
Wertes in einen Text) benannt.
\end{itemize}
Andere \"ubliche Muster sind der \emph{camelCase}, bei dem die
Anf\"ange zusammengesetzter Worte jeweils gro{\ss} geschrieben werden
oder auch die Verwendung von Unterstrichen zur Trennung von
Namenskomponenten. Eine Funktion, die die Anzahl von
Aktionspotentialen bestimmt k\"onnte etwa \file{spikeCount.m} oder
\file{spike\_count.m} benannt werden.
\section{Namensgebung von Variablen und Konstanten}
F\"ur die Bennennung von Variablen und Konstanten gelten die gleichen
Regeln wie f\"ur die Namen von Funktionen und Skripten. Die Maxime von
gutem Programmierstil ist: \emph{``Programmcode muss lesbar
sein.''}. Dabei helfen gute Namen ungemein. Auch wenn es schwer
f\"allt passende und trotzdem nicht zu lange Namen zu finden, sollte
einer gute Namensgebung sehr ernst genommen werden.
W\"ahrend die Namen von Funktionen und Skripten ihren Zweck
beschreiben, sollten die Namen von Variablen ihren Inhalt
beschreiben. Eine Variable, die die mittlere Anzahl von
Aktionspotentialen speichert, k\"onnte also
\varcode{average\_spike\_count} hei{\ss}en. Wenn die Variable nicht
nur einen sondern mehrere Werte aufnimmt, dann ist der Plural
angebracht (\varcode{average\_spike\_counts}).
Die Laufvariablen von \code{for}-Schleifen werden oft nur \varcode{i},
\varcode{j} oder \varcode{k} benannt und sollten aber die einzige Ausnahme
bzgl. ausdrucksstarker Namensgebung bleiben.
\begin{important}[Benennung von Variablen]
Die Namen von Variablen sollten m\"oglichst viel \"uber ihren Inhalt
aussagen (\varcode{spike\_count} statt \varcode{x}). Gute Namen
f\"ur Variablen sind die beste Dokumentation.
\end{important}
\section{Codestil}
Die Lesbarkeit von Programmen wird sehr durch den Codestil
beeinflusst. Ein Programm, in dem z.B. Schleifenk\"orper nicht (oder
zuf\"allig) einger\"uckt sind ist deutlich schwerer zu lesen und zu
verstehen, als eines, in dem eine konsistente Einr\"uckung vorgenommen
wurde. Mit der Tastenkombination \keycode{Ctrl-I} (\keycode{Strg-I}
auf der deutschen Tastatur) kann ein markierter Bereich im \matlab{}
Editor automatisch richtig einger\"uckt werden.
Sparsam und konsistent eingef\"ugte einzelne Leerzeilen sind
hervorragend geeignet, um logische Abschnitte eines Programm zu
trennen. Zu viele Leerzeilen haben den Nachteil, dass das Programm
nicht mehr auf eine Seite passt und dadurch leichter der \"Uberblick
verlorgen geht.
Die beiden folgenden Listings \ref{chaoticcode} und \ref{cleancode}
zeigen die Implementation des random-walk einmal eher chaotisch und
einmal aufger\"aumt und \"ubersichtlich.
\begin{lstlisting}[label=chaoticcode, caption={Un\"ubersichtliche Implementation des Random-walk.}]
num_runs = 10;
max_steps = 1000;
positions = zeros(max_steps, num_runs);
for run = 1:num_runs
for step = 2:max_steps
x = randn(1);
if x<0
positions(step, run)= positions(step-1, run)+1;
elseif x>0
positions(step,run)=positions(step-1,run)-1;
end
end
end
\end{lstlisting}
\pagebreak[4]
\begin{lstlisting}[label=cleancode, caption={\"Ubersichtliche Implementation des Random-walk.}]
num_runs = 10;
max_steps = 1000;
positions = zeros(max_steps, num_runs);
for run = 1:num_runs
for step = 2:max_steps
x = randn(1);
if x < 0
positions(step, run) = positions(step-1, run) + 1;
elseif x > 0
positions(step, run) = positions(step-1, run) - 1;
end
end
end
\end{lstlisting}
\section{Verwendung von Kommentaren}
Kommentarzeilen werden in \matlab{} mit dem Prozentzeichen \code{\%}
gekennzeichnet. Gezielt und sparsam eingesetzte Kommentare sind f\"ur
das Verst\"andnis eines Programms sehr n\"utzlich. Am wichtigsten
sind kurze Kommentare, die den Zweck und das Ziel eines Abschnitts im
Programm erl\"autern (z.B. \varcode{\% compute mean firing rate over all
trials}).
Viele und h\"aufige Kommentare k\"onnen in der Entwicklungsphase eines
Programms sehr hilfreich sein, bl\"ahen aber den Code auf. Durch die
Verwendung guter Variablen- und Funktionsnamen sollten die meisten
Zeilen sowieso weitestgehend selbsterkl\"arend sein.
Die beste Dokumentation ist der Code selbst. Gut geschriebener Code
mit ausdrucksstarken Variablen- und Funktionsnamen ben\"otigt keine
Kommentare, um den Zweck einzelner Zeilen zu erkl\"aren. z.B. ist\\
\varcode{ x = x + 2; \% add two to x}\\
ein v\"ollig unn\"otiger Kommentar.
\begin{important}[Verwendung von Kommentaren]
\begin{itemize}
\item Kommentare sollen die Absicht eines Programmabschnitts beschreiben.
\item Kommentare sind gut und wichtig --- sie m\"ussen aber richtig
sein!
\item Ein falscher Kommentar ist schlimmer als gar kein Kommentar!
\item Kommentare m\"ussen gepflegt werden, sonst sind sie wertlos!
\end{itemize}
\widequote{Good code is its own best documentation. As you're about to add
a comment, ask yourself, ``How can I improve the code so that this
comment isn't needed?'' Improve the code and then document it to
make it even clearer.}{Steve McConnell}
\end{important}
\pagebreak[4]
\section{Dokumentation von Funktionen}
Bei allen vordefinierten \matlab{} Funktionen findet sich am Anfang
eine Kommentarblock, der den Zweck der Funktion, die verschiedenen
M\"oglichkeiten des Funktionsaufrufs und die Argumente und
R\"uckgabewerte beschreibt. Mit dem \code{help}- Befehl wird dieser
Kommentarblock angezeigt. Auch in eigenen Funktionen sind
diese Kommentare sehr wichtig. Siehe Listing~\ref{localfunctions}
f\"ur ein Beispiel einer gut dokumentierten Funktion.
\begin{important}[Dokumentation von Funktionen]
Funktionen m\"ussen unbedingt kommentiert werde!
\begin{itemize}
\item In wenigen Zeilen kurz den Zweck der Funktion beschreiben.
\item Den Funktionskopf nocheinmal hinschreiben, damit
klar ist, in welcher Reihenfolge Argumente \"ubergeben werden.
\item F\"ur jedes Funktionsargument die Bedeutung, der erwartete
Datentyp (Zahl, Vektor, Matrix, etc.), und eventuell die Einheit,
in der die Zahlen erwartet werden (z.B. Sekunden).
\item Ebenso m\"ussen die R\"uckgabewerte beschrieben werden.
\end{itemize}
\end{important}
\section{Auslagerung von Aufgaben in Funktionen}
Kommentare oder Leerzeilen werden benutzt, um logische Abschnitte des
Codes abzutrennen und kurz zu erkl\"aren. Wenn eine
solche inhaltliche Trennung einzuf\"ugt wird, sollte man sich immer fragen,
ob dieser Teil des Programms nicht in eine eigene Funktion ausgelagert
werden sollte. Fast immer kann dies bejaht werden.
Abschnitte nicht auszulagern f\"uhrt zu sehr langen
\codeterm{m-files}, die leicht un\"ubersichtlich werden. Diese Art von
Code wird \codeterm{Spaghetticode} genannt. Es ist h\"ochste Zeit
\"uber Auslagerung in Funktionen nachzudenken.
\begin{important}[Gliederung in Funktionen]
Wann sollten Programmteile in eigene Funktionen ausgelagert werden?
\begin{itemize}
\item Wenn innerhalb einer Funktion oder eines Skripts mehr als zwei
Einr\"uckungsebenen gebraucht werden.
\item Wenn sich Strukturen im Code mehr als einmal wiederholten.
\item Wenn man versucht ist, wiederholte Strukturen mit Copy and Paste zu erzeugen.
\end{itemize}
\end{important}
\subsection{Lokale Funktionen und geschachtelte Funktionen}
Das Auslagern von Funktionalit\"at in eigene Funktionen f\"uhrt dazu,
dass eine F\"ulle von Dateien erzeugt wird, die die
\"Ubersichtlichkeit nicht unbedingt erh\"oht. Wenn die auszulagernde
Funktionalit\"at an vielen Stellen ben\"otigt wird ist es dennoch sehr
sinnvoll dies zu tun. Wenn Funktionen nur von einzelnen anderen
Funktionen verwendet werden, dann bietet \matlab{} die M\"oglichkeit
sogenannte \codeterm[Funktion!lokale]{lokale Funktionen} oder auch
\codeterm[Funktion!geschachtelte]{geschachtelte Funktionen}
(\enterm{nested functions}) in einer einzelnen Datei zu
erstellen. Listing \ref{localfunctions} zeigt ein Beispiel f\"ur eine
lokale Funktion.
\pagebreak[3]
\lstinputlisting[label=localfunctions, caption={Beispiel f\"ur den
Einsatz von lokalen Funktionen.}]{calculateSines.m}
Lokale Funktionen existieren in der gleichen Datei und sind nur dort
verf\"ugbar. Jede Funktion hat ihren eigenen G\"ultigkeitsbereich, das
hei{\ss}t, dass Variablen aus den aufrufenden Funktionen nicht
sichtbar sind.
Bei sogenannten \codeterm[Funktion!geschachtelte]{geschachtelten
Funktionen} ist das anders. Diese werden innerhalb eines
Funktionsk\"orpers (zwischen den Schl\"usselworten \code{function} und
dem \code{end} definiert und k\"onnen auf alle Variablen der
``Mutterfunktion'' zugreifen und diese auch ver\"andern. Folglich
sollten sie nur mit Bedacht eingesetzt werden.
\section{Besonderheiten bei Skripten}
Ein \"ahnliches Problem wurde schon bei der Einf\"uhrung der Skripte
erw\"ahnt. Variablen, die in Skripten definiert werden sind global im
\codeterm{Workspace} verf\"ugbar. Es besteht die Gefahr von
Namenskollisionen. Problem dabei ist, dass der Nutzer gar nicht
mitbekommt, wenn eine Variable redefiniert oder neuen Inhalt
zugewiesen bekommt. Fehler, die auf derartigen Kollisionen beruhen
sind h\"aufig nur schwer zu finden, da das Programm f\"ur sich korrekt
aussieht.
Um dieses Problem zu vermeiden sollten Skripte genauso wie Funktionen
eine spezifische Aufgabe unabh\"angig vom Kontext erf\"ullen. Diese
Aufgabe ist dann nat\"urlich komplexer als die einer
Funktion. z.B. k\"onnte die Aufgabe eines Skriptes sein, die
Spiketrains aller aufgenommenen Zellen zu analysieren. Gute Skripte
sind trotzdem nicht \"uberm\"a{\ss}ig lang und deshalb leicht zu
verstehen.
Ein weiterer, sehr wichtiger Vorteil von zweckbestimmten Skripten ist,
dass sie immer als ganzes ausf\"uhrbar sind --- am einfachsten mit
\keycode{F5} aus dem \matlab-Editor heraus. Wenn ein Fehler auftritt
ist in der Fehlermeldung die Zeilennummer des fehlerhaften Codes
angegeben. Das ist eine sehr wichtige Information, um den Fehler
beheben zu k\"onnen.
\"Ubergeordnete Skripte k\"onnen dann einfach nacheinander
spezifischere Skripte aufrufen. Durch die Namen der aufgerufenen
Skripte ist dann klar, was passieren wird, und durch die
Unabh\"angigkeit der Skripte kommt es nicht zu Kollisionen.
\begin{important}[Struktur von Skripten]
\begin{itemize}
\item Skripte sollten genauso wie Funktionen spezifische Aufgaben
l\"osen und nicht zu lang sein.
\item Skripte sollten unabh\"angig von irgendwelchen Variablen im
\codeterm{Workspace} f\"ur sich alleine geschlossen lauff\"ahig
sein.
\item Es empfiehlt sich zu Beginn eines Skriptes alle Variablen im
\codeterm{Workspace} zu l\"oschen (\code{clear}). Meist ist auch
ein \code{close all}, um alle Figures zu schlie{\ss}en,
angebracht.
\item Am Ende eines Skriptes sollte der \codeterm{Workspace}
mithilfe von \code{clear} wieder von all den Variablen ges\"aubert
werden, die nicht mehr ben\"otigt werden.
\end{itemize}
\end{important}
\section{Fazit}
Programmcode soll lesbar sein. Namen von Variablen, Funktionen und
Skripten sollten ausdrucksstark sein und R\"uckschl\"usse auf den
Inhalt oder den Zweck erlauben. Einen pers\"onlichen Programmierstil
zu entwickeln ist v\"ollig in Ordnung solange er konsistent ist. In
machen Programmiersprachen gibt es Traditionen und \"Ubereink\"unfte,
diese sollten dann beachtet werden.
Wiederholte Programmabschnitte sollten in Funktionen ausgelagert
werden. Wenn diese nicht von globalem Interesse sind, kann mit
\codeterm[Funktion!lokale]{lokalen} oder
\codeterm[Funktion!geschachtelte]{geschachtelten
Funktionen} die \"Ubersichtlichkeit erh\"oht werden.
Es lohnt sich auf den eigenen Programmierstil zu
achten!\footnote{Buchtip: Robert C. Martin: \textit{Clean Code: A
Handbook of Agile Software Craftmanship}, Prentice Hall}
\shortquote{Programs must be written for people to read, and only
incidentally for machines to execute.}{Abelson / Sussman}
\shortquote{Any fool can write code that a computer can
understand. Good programmers write code that humans can
understand.}{Martin Fowler}
\shortquote{First, solve the problem. Then, write the code.}{John
Johnson}
\selectlanguage{english}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB