{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "divine-arthur",
   "metadata": {},
   "source": [
    "# Code kommentieren"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "optimum-command",
   "metadata": {},
   "source": [
    "## Kommentare: was, wie, warum?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "scientific-convertible",
   "metadata": {},
   "source": [
    "### Was?\n",
    "\n",
    "- Erklärung oder Anmerkung im Quellcode\n",
    "- sollen Verständlichkeit für Menschen verbessern\n",
    "- werden von Compilern/Interpretern i.d.R. ignoriert\n",
    "\n",
    "```python\n",
    "#\n",
    "# traverses a directory and collects title names\n",
    "#\n",
    "def traverse(directory, titles):\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "competitive-emission",
   "metadata": {},
   "source": [
    "### Wie? → Syntax\n",
    "\n",
    "#### Zeilenkommentare\n",
    "\n",
    "```python\n",
    "# am Zeilenanfang\n",
    "\n",
    "print(\"Hello World!\") # am Ende einer Zeile\n",
    "```\n",
    "\n",
    "#### Blockkommentare\n",
    "\n",
    "```python\n",
    "def dothis():\n",
    "    \"\"\"Als Beschreibung einer Funktion.\"\"\"\n",
    "    pass\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "vulnerable-congress",
   "metadata": {},
   "source": [
    "### Warum Code kommentieren?\n",
    "\n",
    "> Good code is its own best documentation. ([Steve McConnell](http://en.wikipedia.org/wiki/Steve_McConnell))\n",
    "\n",
    "- viele widersprüchliche Meinungen, z.B.\n",
    "  - möglichst selbsterklärender Code (\"self-documenting\") mit wenigen Kommentaren\n",
    "  - ausführliche Kommentare und Erklärungen\n",
    "- wichtig: \n",
    "  - Kommentare müssen korrekt sein und zum Code passen\n",
    "  - überflüssige oder nicht hilfreiche Kommentare vermeiden\n",
    "  - Zielgruppe beachten!\n",
    "    - Einführungstext für Anfänger*innen:\n",
    "  \n",
    "      ```java\n",
    "      String s = \"Wikipedia\"; /* Assigns the value \"Wikipedia\" to the variable s. */\n",
    "      ```\n",
    "\n",
    "- https://web.archive.org/web/20170709211040/https://mikegrouchy.com/blog/2013/03/yes-your-code-does-need-comments.html"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "competitive-binding",
   "metadata": {},
   "source": [
    "## Zweck von Kommentaren"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "designed-shirt",
   "metadata": {},
   "source": [
    "### Code planen und prüfen\n",
    "\n",
    "```python\n",
    "# alle Einträge rückwärts (chronologisch!) durchlaufen\n",
    "for eintrag in reversed(logbuch):\n",
    "    # Eintrag verarbeiten\n",
    "    update(eintrag)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "insured-bronze",
   "metadata": {},
   "source": [
    "- vor dem Programmieren in einer Art Pseudocode den zu schreibenden Code skizzieren\n",
    "- sollte den Sinn des Codes beschreiben, nicht den Code selbst\n",
    "- ermöglicht Begutachtung/Prüfung des fertigen Codes → erfüllt dieser seine Aufgabe?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "roman-astrology",
   "metadata": {},
   "source": [
    "## Code beschreiben\n",
    "\n",
    "> Don't document bad code – rewrite it. ([The Elements of Programming Style](https://en.wikipedia.org/wiki/The_Elements_of_Programming_Style_(book)), Kernighan & Plauger)\n",
    "\n",
    "> Good comments don't repeat the code or explain it. They clarify its intent. Comments should explain, at a higher level of abstraction than the code, what you're trying to do. ([Code Complete](https://en.wikipedia.org/wiki/Code_Complete), McConnell)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "deluxe-replica",
   "metadata": {},
   "source": [
    "```python\n",
    "   \"status\" : d[\"status\"] # unused but required by standard\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "practical-appointment",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Wir benötigen eine stabile Sortierung. Geschwindigkeit spielt hier keine Rolle.\n",
    "insertion_sort(liste) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "moderate-forwarding",
   "metadata": {},
   "source": [
    "- Code zusammenfassen oder Absicht erklären\n",
    "- NICHT: Code in natürlicher Sprache wiederholen\n",
    "- auch: (Ursache für) Besonderheiten erklären\n",
    "- auch: Algorithmus beschreiben  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "black-constraint",
   "metadata": {},
   "source": [
    "## Einbettung von Ressourcen\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "concerned-narrative",
   "metadata": {},
   "source": [
    "## Metadaten"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "indirect-stevens",
   "metadata": {},
   "outputs": [],
   "source": [
    "#\n",
    "# Process (extract, filter, merge) Vossantos in an org mode file.\n",
    "#\n",
    "# Usage: Without any arguments, extracts all Vossanto canidates from\n",
    "# the given org file.\n",
    "#\n",
    "# Author: rja\n",
    "#\n",
    "# Changes:\n",
    "# 2019-12-18 (rja)\n",
    "# - added field sourceImageLicense\n",
    "# 2019-12-16 (rja)\n",
    "# - added option \"--images\" to enrich URLs for Wikipedia Commons images\n",
    "# ..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exterior-intervention",
   "metadata": {},
   "source": [
    "Quelle: https://github.com/weltliteratur/vossanto/blob/master/org.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "several-device",
   "metadata": {},
   "source": [
    "- Namen der Autor*innen, Versionshinweise, Lizenzangaben, Link zur Dokumentation, etc.\n",
    "- Hinweise zur Nutzung, zu Fehlern/Verbesserungsmöglichkeiten, zum Melden von Fehlern, etc."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "applicable-purpose",
   "metadata": {},
   "source": [
    "## Debugging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "following-prophet",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_matching_item(bibentry, items):\n",
    "    # heuristic: rely on Crossref's ranking and take the first item\n",
    "    item = items[0]\n",
    "    # heuristic: do (almost) exact string matching on the title (only)\n",
    "    if item.get(\"title\")[0].casefold() == bibentry[\"title\"].casefold():\n",
    "        return item\n",
    "    # debug output\n",
    "    # print(bibentry[\"ID\"], bibentry[\"title\"])\n",
    "    # print(\"  best Crossref match:\", item.get(\"title\"), item.get(\"DOI\"))\n",
    "    return None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "advance-murder",
   "metadata": {},
   "source": [
    "- auskommentierter Code wird nicht ausgeführt\n",
    "- Fehlersuche; \"alten\" oder alternativen Code deaktivieren"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "contemporary-limitation",
   "metadata": {},
   "source": [
    "## Automatische Erzeugung von Dokumentation\n",
    "\n",
    "Quelle: https://scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_curve.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "silent-catalyst",
   "metadata": {},
   "outputs": [],
   "source": [
    "def roc_curve(y_true, y_score, *, pos_label=None, sample_weight=None,\n",
    "              drop_intermediate=True):\n",
    "    \"\"\"Compute Receiver operating characteristic (ROC)\n",
    "\n",
    "    Note: this implementation is restricted to the binary classification task.\n",
    "\n",
    "    Read more in the :ref:`User Guide <roc_metrics>`.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "\n",
    "    y_true : array, shape = [n_samples]\n",
    "        True binary labels. If labels are not either {-1, 1} or {0, 1}, then\n",
    "        pos_label should be explicitly given.\n",
    "\n",
    "    y_score : array, shape = [n_samples]\n",
    "        Target scores, can either be probability estimates of the positive\n",
    "        class, confidence values, or non-thresholded measure of decisions\n",
    "        (as returned by \"decision_function\" on some classifiers).\n",
    "\n",
    "    pos_label : int or str, default=None\n",
    "        The label of the positive class.\n",
    "        When ``pos_label=None``, if y_true is in {-1, 1} or {0, 1},\n",
    "        ``pos_label`` is set to 1, otherwise an error will be raised.\n",
    "\n",
    "    sample_weight : array-like of shape (n_samples,), default=None\n",
    "        Sample weights.\n",
    "\n",
    "    drop_intermediate : boolean, optional (default=True)\n",
    "        Whether to drop some suboptimal thresholds which would not appear\n",
    "        on a plotted ROC curve. This is useful in order to create lighter\n",
    "        ROC curves.\n",
    "\n",
    "        .. versionadded:: 0.17\n",
    "           parameter *drop_intermediate*.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    fpr : array, shape = [>2]\n",
    "        Increasing false positive rates such that element i is the false\n",
    "        positive rate of predictions with score >= thresholds[i].\n",
    "\n",
    "    tpr : array, shape = [>2]\n",
    "        Increasing true positive rates such that element i is the true\n",
    "        positive rate of predictions with score >= thresholds[i].\n",
    "\n",
    "    thresholds : array, shape = [n_thresholds]\n",
    "        Decreasing thresholds on the decision function used to compute\n",
    "        fpr and tpr. `thresholds[0]` represents no instances being predicted\n",
    "        and is arbitrarily set to `max(y_score) + 1`.\n",
    "\n",
    "    See also\n",
    "    --------\n",
    "    roc_auc_score : Compute the area under the ROC curve\n",
    "\n",
    "    Notes\n",
    "    -----\n",
    "    Since the thresholds are sorted from low to high values, they\n",
    "    are reversed upon returning them to ensure they correspond to both ``fpr``\n",
    "    and ``tpr``, which are sorted in reversed order during their calculation.\n",
    "\n",
    "    References\n",
    "    ----------\n",
    "    .. [1] `Wikipedia entry for the Receiver operating characteristic\n",
    "            <https://en.wikipedia.org/wiki/Receiver_operating_characteristic>`_\n",
    "\n",
    "    .. [2] Fawcett T. An introduction to ROC analysis[J]. Pattern Recognition\n",
    "           Letters, 2006, 27(8):861-874.\n",
    "\n",
    "    Examples\n",
    "    --------\n",
    "    >>> import numpy as np\n",
    "    >>> from sklearn import metrics\n",
    "    >>> y = np.array([1, 1, 2, 2])\n",
    "    >>> scores = np.array([0.1, 0.4, 0.35, 0.8])\n",
    "    >>> fpr, tpr, thresholds = metrics.roc_curve(y, scores, pos_label=2)\n",
    "    >>> fpr\n",
    "    array([0. , 0. , 0.5, 0.5, 1. ])\n",
    "    >>> tpr\n",
    "    array([0. , 0.5, 0.5, 1. , 1. ])\n",
    "    >>> thresholds\n",
    "    array([1.8 , 0.8 , 0.4 , 0.35, 0.1 ])\n",
    "\n",
    "    \"\"\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "composite-reservation",
   "metadata": {},
   "source": [
    "## Anweisungen für bestimmte Programme"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "military-minister",
   "metadata": {},
   "outputs": [],
   "source": [
    "#!/usr/bin/env python3\n",
    "# -*- coding: UTF-8 -*-\n",
    "# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4\n",
    "\n",
    "print(\"Testing\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conservative-dayton",
   "metadata": {},
   "source": [
    "- \"Shebang\" → gibt den zu verwendenden Interpreter an\n",
    "- \"Magic comments\", z.B. zur verwendeten Codierung\n",
    "- ... oder zur Konfiguration des Editors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aging-malawi",
   "metadata": {},
   "source": [
    "## Stressabbau :-)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "phantom-iraqi",
   "metadata": {},
   "source": [
    "```c\n",
    "\t/*\n",
    "\t * Some dipshit decided to store some other bit of information\n",
    "\t * in the high byte of the file length.  Truncate size in case\n",
    "\t * this CDROM was mounted with the cruft option.\n",
    "\t */\n",
    "```\n",
    "Quelle: https://github.com/torvalds/linux/blob/master/fs/isofs/inode.c#L1396\n",
    "→ [Linux Swear Count](http://www.vidarholen.net/contents/wordcount/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "julian-particle",
   "metadata": {},
   "source": [
    "```c\n",
    "// \n",
    "// Dear maintainer:\n",
    "// \n",
    "// Once you are done trying to 'optimize' this routine,\n",
    "// and have realized what a terrible mistake that was,\n",
    "// please increment the following counter as a warning\n",
    "// to the next guy:\n",
    "// \n",
    "// total_hours_wasted_here = 42\n",
    "// \n",
    "```\n",
    "Quelle: https://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "directed-lithuania",
   "metadata": {},
   "source": [
    "## *Anleitung zum Unglücklichsein* oder *How to write unmaintainable code*\n",
    "\n",
    "> Any fool can tell the truth, but it requires a man of some sense to know how to lie well. ([Samuel Butler](https://en.wikipedia.org/wiki/Samuel_Butler_(novelist)) (1835 - 1902))\n",
    "\n",
    "> Incorrect documentation is often worse than no documentation. ([Bertrand Meyer](https://en.wikipedia.org/wiki/Bertrand_Meyer))\n",
    "\n",
    "Since the computer ignores comments and documentation, you can lie outrageously and do everything in your power to befuddle the poor maintenance programmer.\n",
    "\n",
    "Quelle: Roedy Green, [How To Write Unmaintainable Code](https://web.archive.org/web/20120306115925/http://freeworld.thc.org/root/phun/unmaintain.html) ([Originalseite](https://www.mindprod.com/jgloss/unmain.html))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "unusual-excellence",
   "metadata": {},
   "source": [
    "###  Lie in the comments\n",
    "\n",
    "You don't have to actively lie, just fail to keep comments as up to date with the code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "boolean-cleaning",
   "metadata": {},
   "outputs": [],
   "source": [
    "# remove control characters\n",
    "def rm_ctrl(text):\n",
    "    return re.sub(r\"[\\n\\t\\r]+\", \" \", text).strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "attempted-mount",
   "metadata": {},
   "source": [
    "### Document the obvious\n",
    "\n",
    "Pepper the code with comments like `/* add 1 to i */` however, never document wooly stuff like the overall purpose of the package or method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aerial-utility",
   "metadata": {},
   "outputs": [],
   "source": [
    "def einefunktion(a, b):\n",
    "    # a² zu b² addieren\n",
    "    c = a**2 + b**2\n",
    "    # die Wurzel des Ergebnisses zurückgeben\n",
    "    return math.sqrt(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "linear-johns",
   "metadata": {},
   "outputs": [],
   "source": [
    "def einefunktion(a, b):\n",
    "    # Für zwei gegebene Seitenlängen a und b die dritte Seitenlänge\n",
    "    # c eines rechtwinkligen Dreiecks mit Hilfe des Satzes des \n",
    "    # Pythagoras (a² + b² = c²) berechnen.\n",
    "    c = a**2 + b**2\n",
    "    return math.sqrt(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ahead-sheep",
   "metadata": {},
   "source": [
    "### Document How Not Why\n",
    "\n",
    "Document only the details of what a program does, not what it is attempting to accomplish. That way, if there is a bug, the fixer will have no clue what the code should be doing."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "still-marketplace",
   "metadata": {},
   "source": [
    "### Avoid Documenting the \"Obvious\"\n",
    "\n",
    "If, for example, you were writing an airline reservation system, make sure there are at least 25 places in the code that need to be modified if you were to add another airline. Never document where they are. People who come after you have no business modifying your code without thoroughly understanding every line of it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "interracial-translator",
   "metadata": {},
   "source": [
    "### On the Proper Use Of Documentation Templates\n",
    "\n",
    "Consider function documentation prototypes used to allow automated documentation of the code. These prototypes should be copied from one function (or method or class) to another, but never fill in the fields. If for some reason you are forced to fill in the fields make sure that all parameters are named the same for all functions, and all cautions are the same but of course not related to the current function at all."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "charged-conviction",
   "metadata": {},
   "source": [
    "### On the Proper Use of Design Documents\n",
    "\n",
    "When implementing a very complicated algorithm, use the classic software engineering principles of doing a sound design before beginning coding. Write an extremely detailed design document that describes each step in a very complicated algorithm. The more detailed this document is, the better.\n",
    "\n",
    "In fact, the design doc should break the algorithm down into a hierarchy of structured steps, described in a hierarchy of auto-numbered individual paragraphs in the document. Use headings at least 5 deep. Make sure that when you are done, you have broken the structure down so completely that there are over 500 such auto-numbered paragraphs. For example, one paragraph might be(this is a real example)\n",
    "\n",
    "1.2.4.6.3.13 - Display all impacts for activity where selected mitigations can apply (short pseudocode omitted).\n",
    "\n",
    "**then...** (and this is the kicker) when you write the code, for each of these paragraphs you write a corresponding global function named:\n",
    "\n",
    "Act1_2_4_6_3_13() Do not document these functions. After all, that's what the design document is for!\n",
    "\n",
    "Since the design doc is auto-numbered, it will be extremely difficult to keep it up to date with changes in the code (because the function names, of course, are static, not auto-numbered.) This isn't a problem for you because you will not try to keep the document up to date. In fact, do everything you can to destroy all traces of the document.\n",
    "\n",
    "Those who come after you should only be able to find one or two contradictory, early drafts of the design document hidden on some dusty shelving in the back room near the dead 286 computers."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "rolled-starter",
   "metadata": {},
   "source": [
    "### Units of Measure\n",
    "\n",
    "Never document the units of measure of any variable, input, output or parameter. e.g. feet, metres, cartons. This is not so important in bean counting, but it is very important in engineering work. As a corollary, never document the units of measure of any conversion constants, or how the values were derived. It is mild cheating, but very effective, to salt the code with some incorrect units of measure in the comments. If you are feeling particularly malicious, make up your **own** unit of measure; name it after yourself or some obscure person and never define it. If somebody challenges you, tell them you did so that you could use integer rather than floating point arithmetic."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eligible-graduate",
   "metadata": {},
   "source": [
    "### Gotchas\n",
    "\n",
    "Never document gotchas in the code. If you suspect there may be a bug in a class, keep it to yourself. If you have ideas about how the code should be reorganised or rewritten, for heaven's sake, do not write them down. Remember the words of Thumper in the movie Bambi *\"If you can't say anything nice, don't say anything at all\"*. What if the programmer who wrote that code saw your comments? What if the owner of the company saw them? What if a customer did? You could get yourself fired. An anonymous comment that says \"This needs to be fixed!\" can do wonders, especially if it's not clear what the comment refers to. Keep it vague, and nobody will feel personally criticised."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "incident-queue",
   "metadata": {},
   "source": [
    "### Documenting Variables\n",
    "Never put a comment on a variable declaration. Facts about how the variable is used, its bounds, its legal values, its implied/displayed number of decimal points, its units of measure, its display format, its data entry rules (e.g. total fill, must enter), when its value can be trusted etc. should be gleaned from the procedural code. If your boss forces you to write comments, lard method bodies with them, but never comment a variable declaration, not even a temporary!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "german-knight",
   "metadata": {},
   "source": [
    "```c\n",
    "const char *name;\n",
    "int len;\n",
    "char c;\n",
    "unsigned long hash;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "popular-bracelet",
   "metadata": {},
   "source": [
    "### Disparage In the Comments\n",
    "\n",
    "Discourage any attempt to use external maintenance contractors by peppering your code with insulting references to other leading software companies, especial anyone who might be contracted to do the work. e.g.:\n",
    "```java\n",
    "        /* The optimised inner loop.\n",
    "        This stuff is too clever for the dullard at Software Services Inc., who would\n",
    "        probably use 50 times as memory & time using the dumb routines in <math.h>.\n",
    "        */\n",
    "        class clever_SSInc\n",
    "            {\n",
    "            .. .\n",
    "            } \n",
    "```\n",
    "\n",
    "If possible, put insulting stuff in syntactically significant parts of the code, as well as just the comments so that management will probably break the code if they try to sanitise it before sending it out for maintenance."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "human-thomson",
   "metadata": {},
   "source": [
    "### COMMENT AS IF IT WERE CØBØL ON PUNCH CARDS\n",
    "\n",
    "Always refuse to accept advances in the development environment arena, especially SCIDs. Disbelieve rumors that all function and variable declarations are never more than one click away and always assume that code developed in Visual Studio 6.0 will be maintained by someone using edlin or vi. Insist on Draconian commenting rules to bury the source code proper."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "unusual-dairy",
   "metadata": {},
   "source": [
    "### Monty Python Comments\n",
    "\n",
    "On a method called makeSnafucated insert only the JavaDoc `/* make snafucated */`. Never define what *snafucated* means **anywhere**. Only a fool does not already know, with complete certainty, what *snafucated* means. For classic examples of this technique, consult the Sun AWT JavaDOC. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "protecting-cleaners",
   "metadata": {},
   "source": [
    "## Kommentare beschreiben\n",
    "\n",
    "Codebeispiele vorgeben, dann selber:\n",
    "- was is der Zweck der Kommentare? Welche Art von Kommentar? "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "human-redhead",
   "metadata": {},
   "source": [
    "## Code selber kommentieren"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "developed-figure",
   "metadata": {},
   "source": [
    "## Literatur\n",
    "\n",
    "- https://en.wikipedia.org/wiki/Comment_(computer_programming)\n",
    "- Keyes, Jessica (2003). Software Engineering Handbook. CRC Press. ISBN 978-0-8493-1479-7.\n",
    "- Roedy Green, [How To Write Unmaintainable Code](https://web.archive.org/web/20120306115925/http://freeworld.thc.org/root/phun/unmaintain.html) ([Originalseite](https://www.mindprod.com/jgloss/unmain.html))\n",
    "- http://mikegrouchy.com/blog/2013/03/yes-your-code-does-need-comments.html\n",
    "- https://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}