Denken lernen durch Informatik, oder: Die Maulwurfswette

[Vorbemerkung: Ich hatte versprochen, einen Blog-Artikel darüber zu schreiben, warum es mir so wichtig ist, dass alle Schüler Informatikunterricht erhalten, warum ich also für ein #PflichtfachInformatik eintrete.  Aber wie es so geht: The tale grew in the telling… Was ihr vor euch habt, ist deshalb nur der erste Teil einer mehrteiligen Serie.  Gut daran ist, dass die Sache dadurch etwas interaktiver werden kann – schließlich ist das hier das Internet!  Wenn ihr also — was mich sehr freuen würde — hier kommentiert, wird dieses Feedback in die zukünftigen Teile einfließen.  Aber zuerst bin ich dran.  Also: Vorhang auf!]


 

Eine Reihenhaussiedlung.  Charlie klingelt bei Ada, einer Informatiklehrerin.  Sie öffnet.

Charlie: Ah, Ada, schön, dass ich dich zuhause antreffe.  Du hast du doch sicher einen Augenblick Zeit, nicht?

Ada: Hallo, Charlie. Eigentlich wollte ich gerade im Garten etwas Wichtiges erledigen… aber bitte, komm rein.

Charlie: Danke. Ich wollte dir nur erzählen, dass ich jetzt, glaube ich, verstehe, warum du so vehement für Informatikunterricht in der Schule eintrittst:  Die Feiertage mit meinen Nichten und Neffen… grausam…

Ada: So, du verstehst mich… Na, ich fürchte, ein Augenblick wird für unser Gespräch nicht ausreichen.  (Nimmt eine riesige Pfeffermühle aus dem Regal.) Aber komm doch einfach mit in den Garten; wir können uns dort unterhalten, während ich den Rasen maulwurfsicher mache.

Charlie: Wie? Mit der Pfeffermühle? Na, egal.  Meine Nichten jedenfalls… Also die Jugendlichen von heute schauen von ihren Smartphones ja gar nicht mehr hoch.  Total kommunikationsunfähig, nur noch körperlich anwesend, ansonsten in der Matrix.  Früher, als man mit den Dingern nur telefonieren konnte, ging’s ja noch.  Aber inzwischen sind das ja richtige Computer.  Und ich seh’s jetzt ein: Jemand muss den Kids beibringen, wie man die Dinger sinnvoll verwendet.   Also Ja zum Pflichtfach Informatik!

Sie betreten den Garten.

Ada: Charlie, du hast recht und liegst doch ganz falsch. Medienbildung, wie du sie dir vorstellst, ist natürlich wichtig.  Aber mir geht es um etwas noch Grundsätzlicheres.

Charlie: Du meinst, die Schüler müssen erstmal lernen, wie so ein Computer aufgebaut ist?

Ada: Nein, nein, das meine ich überhaupt nicht! Du weißt schon, dass es in der Informatik nicht um Computer geht?

Charlie: Nicht?

Ada: Nein! Der Computer ist nur das Werkzeug.  Zugegeben, ein komplexes, extrem vielseitiges Werkzeug.  Aber entscheidend ist, was der Mensch vor dem Computer denkt und wie er denkt.

Charlie: Ach, jetzt fängst du gleich mit solchen Haarspaltereien an.  Es läuft doch auf jeden Fall darauf heraus, dass man vor der Kiste hängt.

Ada: Also, erstens verbringen Informatiker, egal ob in der Industrie oder der Wissenschaft, viel weniger Zeit allein vor dem Computer, als du glaubst — aber dazu später mehr.  Zweitens wird das beim Schulfach Informatik ganz genauso sein.  Und drittens: Du bist doch Musik-Experte, nicht?

Charlie: Ich liebe Musik! Von Bach bis zu den Beatles, von Metallica bis Mozart, von van Beethoven bis Van Halen, von…

Ada: (unterbricht) …ist gut, ist gut, ich hab’s kapiert: Musik ist dein Ding.  Musik, die Kunst des Instrumentenbaus, die Wissenschaft von den Stereoanlagen und Hifi-Türmen,  die Lehre von den Plattenlabels und…

Charlie: Was redest du da? Das ist doch alles nicht das, was Musik ausma… Oh. Verstehe.

Ada: Ein berühmter Informatiker soll mal gesagt haben: „In der Informatik geht es genauso wenig um Computer wie in der Astronomie um Teleskope.“  Ein kluger Satz…

Charlie: …und weniger als 140 Zeichen lang!

Ada:  Oder in einer Variante speziell für dich: „Der Computer spielt für den Informatiker die gleiche Rolle wie für einen Komponisten das Orchester.“  Natürlich ist das Orchester wichtig, damit die Musik hörbar wird.  Entscheidend aber sind für den Komponisten sein Denken in musikalischen Strukturen und seine Kreativität.  Genauso ist es in der Informatik.

Charlie: Na wenigstens in Bezug auf Musik scheinen wir uns ja einig zu sein.  Aber du willst  doch nicht ernsthaft etwas so nüchtern-technisches wie Informatik mit Kunst vergleichen, oder?

Ada: „Nüchtern-technisch“ – wenn ich das nur höre, werde ich schon sauer.  Genau dieses Bild von der Informatik ist ja der Anfang des ganzen Übels.  Kein Wunder, dass die meisten Schüler und v.a. die Schülerinnen schon vergrault sind, bevor sie überhaupt eine Chance hatten zu erfahren, was Informatik wirklich ist.

Charlie: Aber was ist sie denn nun wirklich? Und vor allem: Wenn sich Informatik weder mit Smartphones noch mit Computern beschäftigt, die ja nun wirklich für den Alltag eine große Rolle spielen – dann klingt mir das nicht nach einem Fach, das alle Schüler haben müssten.  Würde es nicht ausreichen, wenn diejenigen Informatikunterricht wählen, die mal Programmierer oder sowas werden wollen?  (murmelt) Die Nerds eben?

Ada: Ja, das ist die entscheidende Frage. Meine Antwort darauf: Nein, das reicht nicht, aus vielen Gründen nicht.  Der für mich wichtigste ist, etwas verkürzt gesagt: Informatik kennenlernen, heißt Denken lernen!

Charlie: Oha, steile These!  Die musst du aber erstmal begründen!

Ada: Ich will dir das gerne erklären. Aber jetzt… (nimmt die Pfeffermühle und beginnt auf dem Rasen zu mahlen) …muss ich erstmal, etwas gegen die Maulwürfe unternehmen.

Charlie: Wee Tee Eff?

Ada: Ich habe ein Verfahren entwickelt, um Maulwürfe zu vertreiben.  Du weißt ja, ein paar Straßen weiter sehen manche Gärten bereits aus wie frisch umgepflügt.  Aber ich bin mir sicher, dass man sie fernhalten kann, wenn man den Rasen mit einer ordentlichen Dosis Pfeffer bestäubt.

Charlie: Das meinst du jetzt nicht ernst, oder? Einen größeren Quatsch hab ich ja noch nie gehört! (Murmelt) Ich weiß, wer hier erstmal „Denken lernen“ sollte…

Ada: Wir können ja wetten.

Charlie: Mit Vergnügen.  Wie genau hast du dir das vorgestellt?

Ada: Indem du versucht, meine Theorie zu widerlegen.  Praktischerweise habe ich gestern schon eine Nachbarschaftsversammlung einberufen, um meine Idee den Besitzern der umliegenden Gärten, Anna, Bobo, Coco und Didi vorzustellen…

Charlie: Deine Nachbarn heißen Anna, Bobo, Coco und Didi?

Ada: Ja. Wieso?

Charlie: Die Namen kommen mir irgendwie seltsam vor.

Ada: Weiß nicht, was du meinst.  Na jedenfalls habe ich mein Konzept präsentiert und die anderen wollten drüber nachdenken, ob sie es mal ausprobieren.  Naja, bis auf Didi: Er hat gleich gesagt, dass er die Idee für Schwachsinn hält.

Charlie: Ist mir nicht unsympathisch, der Herr.

Ada: Anna hingegen war gleich Feuer und Flamme, hat sich sofort meine Mühle mit Peugot-Mahlwerk geborgt und ihren Garten ordentlich eingepfeffert

Charlie:  Was ist mit den anderen beiden, Bobo und Coco?

Ada: Mit denen habe ich seitdem noch nicht wieder gesprochen. Aber ihre Gärten grenzen direkt an meinen an.  Schauen wir doch einfach mal über die Zäune. Oh je, Bobos Garten sieht übel aus.  Da war aber mehr als ein Maulwurf am Werk…

Charlie: Dann ist das auf der anderen Seite der Garten von Coco? Der ist aber gepflegt! Finde ich fast schon steril.  Jedenfalls waren hier keine Maulwürfe!

Ada: Jetzt aber zurück zu unserer Wette. Ich schlage vor, wir machen es so: Du sagst mir, welche meiner Nachbarn wir auf jeden Fall besuchen und befragen müssen, um eventuelle Verstöße gegen meine Theorie festzustellen.  Wenn du den oder die richtigen nennst, hast du gewonnen, sonst ich. Einverstanden?

Charlie: Kein Problem. Wir gehen einfach zu allen.  Oder nee, muss ja gar nicht sein, es reicht ja eigentlich wenn wir nur zu… beziehungsweise vielleicht… Äh, warte mal kurz…

Ada: No pressure. Denk ruhig in Ruhe nach.

Charlie: Irgendwie hab ich den Verdacht, hier geht es gar nicht mehr um Maulwürfe und Pfeffer… Die Geschichte dient in Wirklichkeit dazu, mir irgendwas zu demonstrieren, oder?

Ada: Ich? Glaubst du etwa, ich habe die Maulwurfshügel selbst aufgeschüttet?

Charlie: (verlegen) Das nicht gerade… aber das alles hier… deine Nachbarn mit den komischen Namen… das hat sowas Unwirkliches.  Ich bin ganz durcheinander.  Ich glaube, ich möchte, bevor wir losgehen, erst nochmal alle Informationen in einem Diagramm festhalten.

Ada: Eine hervorragende Idee! Auch in der Informatik ist die Wahl der passenden Repräsentation für ein Problem oft schon der halbe Weg zu seiner Lösung.

Das folgende Diagramm entsteht:

maulwuerfe

Charlie: Ah, das hilft mir wirklich. Ich glaube, ich weiß jetzt, wen wir befragen müssen, um deine Theorie zu überprüfen.

Ada: Prima, aber bitte nur diejenigen, deren Aussage uns wirklich weiterhilft, sonst ziehen sie uns gleich wieder in ausschweifende Diskussionen über Mathematikunterricht oder Bürgerrechte im Digitalzeitalter hinein.

Charlie: Schon klar, wir befragen so viele Nachbarn, wie nötig, aber so wenige, wie möglich.

Ada: Nicht, dass du mich falsch verstehst: Meine Nachbarn sind alle total nett.  Leider sind sie auch alle Lehrer — d.h. immer auf Sendung.  Da kommt man so schnell nicht vom Zaun weg. Eigentlich würde ich sie dir auch gerne mal vorstellen; die haben nämlich auch alle was zum Schulfach Informatik zu sagen.  Ich weiß, was mir machen: Ich lade euch alle hier im Garten zum Kaffee ein. Und wer die Wette verliert, muss den Kuchen spendieren.

Charlie: Einverstanden. Komm mit, wir müssen hier entlang…

Ada: Unterwegs kann ich dir ja von meinem großen Idol Seymour Papert erzählen.  Die informatische Denkweise, die ich meinen Schülern beibringen will, hat er mal  als „die Essenz intellektueller Aktivität“ bezeichnet.

Charlie: Klingt ja gut, aber vorerst bleibe ich skeptisch.  Versuch ruhig weiter, mich vom Sinn des Informatikunterrichts zu überzeugen.  Das könnte vielleicht sogar klappen — deine Maulwurfstheorie hingegen werde ich schon an der nächsten Gartenpforte falsifizieren!

Ada: Ah, Popper gelesen? Sehr gut. Dann ist dir natürlich klar, dass du die Wette evtl. einfach verlierst, weil die Maulwürfe noch gar nicht da sind.  Auf diese Weise will ich natürlich nicht gewinnen. Deshalb: Wenn du mich zu genau den Nachbarn führst, bei denen du meine Theorie widerlegen könntest, gebe ich mich geschlagen und du gewinnst.
Hübsches Kleid übrigens, das du heute trägst. Neu?

Charlie: Danke! Mein Freund hat es mir zum Geburtstag geschenkt. Wieso fragst du?

Ada: Ach, nur so.

Sie gehen los.


[Fortsetzung folgt. (Aber wahrscheinlich dauert’s ein bisschen; das Kerngeschäft ruft.)  Bis dahin freue ich mich auf Kommentare hier. Zu welchem oder welchen Nachbarn würdet ihr gehen? (Wer den Ursprung dieser Wette kennt, bitte nicht spoilern!]

 

Das Wunschblog

In meiner Kindheit in den 1980er Jahren gab es im ZDF den Wunschfilm, bei dem die Fernsehzuschauer per TED darüber abstimmen durften, welcher von drei Spielfilmen am Samstagabend gezeigt werden sollte.  Großes basisdemokratisch-partizipatives Fernsehen.  Video-On-Demand 1.0.   Ich habe es geliebt! (Obwohl niemand in meiner Familie auf die Idee gekommen, da womöglich selbst mal anzurufen.)

Die Musik — „dadadam dadadam!“ — zweitverwertet übrigens seitdem die jährliche SWR1-Hitparade, auch so ein Relikt meiner Jugend.

So etwas möchte ich hier auch mal versuchen.  Dadadam dadadam!

Der Leserkreis dieses Blogs ist ja leider recht überschaubar.  Da wäre es doch schade, wenn diejenigen wenigen, die den Weg hierher finden, auf Artikel stießen, die sie gar nicht interessieren. Erst recht, weil ich zwar eine lange Liste von Themen habe, über die ich gerne bloggen würde, aber so selten dazu komme.  Wäre doch schade, wenn ich dann eins aussuche, das keinen interessiert.  Also mache ich es lieber wie das ZDF damals und frage einfach nach. Meine Damen und Herren, liebe Kinder, ich präsentiere Ihnen:

Den Das  ZIDS-Wunschblog! 

Und so läuft’s (dadadam dadadam):  Unten stelle ich euch drei Themen vor. Abstimmen darf jeder, egal ob alter oder neuer Leser, und kostenlos ist’s auch.  Sagt einfach per Kommentar hier im Blog oder per Antwort auf den folgenden Tweet, welches Thema euch am meisten interessiert.  (Kompliziertere Vorting-Systeme à la edchat.de überlege ich mir erst, wenn mehr als 20 Leute mitmachen — und das will ich erst sehen, bevor ich es glaube…)

Für dieses erste (hoffentlich nicht letzte) Wunschblog habe ich drei informatikbezogene Themen ausgewählt, wegen der Stammleserschaft.   Und hier kommen sie.  Dadadam dadadam:


Thema 1: Informatik für alle

Soll Informatik als verpflichtendes Fach in unseren Schule unterrichtet werden? Diese Frage wird (in meiner Ecke des Internet) viel diskutiert und zwar durchaus kontrovers.  Manche wollen in erster Linie mehr Medienpädagogik, andere kämpfen für genuin informatische Inhalte, wieder andere wollen nur das „Coding“ in den Vordergrund stellen.  Der eine sorgt sich um die fehlenden IT-Spezialisten in der deutschen Wirtschaft, der andere um Rechte und Mündigkeit der Bürger in der digitalen Gesellschaft.  Und es gibt nicht wenige Menschen, die finden, dass die Welt auch ohne Informatikunterricht schon digital genug ist.
Ich habe dazu selbstverständlich eine Meinung und eine Position, fast schon eine Mission, die ich schon lange mal verschriftlichen wollte. Jetzt wäre, dadadam dadadam,die Gelegenheit dazu.


Thema 2: Musik machen mit JythonMusic

Vor längerer Zeit habe ich eine Blog-Serie zu, nennen wir es mal, algorithmischer Musiktheorie gestartet – und sie nach dem zweiten Artikel nie wieder fortgesetzt.  Das lag nicht nur an fehlender Zeit, sondern auch daran, dass der Weg Python → LilyPond → Midi-Datei → Töne viel zu umständlich für echtes „Musizieren“ war, so dass mir (trotz vieler Ideen) ein wenig der Spaß vergangen ist.  Inzwischen gibt es aber JythonMusic, ein tolles Projekt, bei dem man (sogar ohne viel zu installieren) sofort loslegen und Musik machen kann.
In den Herbstferien lag ich ein paar Tage im Krankenhaus, war aber fit genug, um endlich mal wieder (ablenkungsfrei) zu programmieren. Ich bzw. mein Computer bzw. wir beide zusammen haben dabei einige interessante Sachen „komponiert“.  Wenn ihr also zu der illustren Gruppe von Leuten gehört, die sich sowohl für ein bisschen Musiktheorie als auch Programmierung interessiert, lasse ich euch, dadadam dadadam, gerne daran teilhaben.


Thema 3: Funktionaler Programmierspaß in Java 8

Java ist als Einstiegsprogrammiersprache in der Schule eine Katastrophe! (IMNSHO). Unter anderem weil ich dachte, dass sähen alle so, habe ich vor drei Jahren an dieser Stelle vermutet, dass „wir“ in fünf Jahren nicht mehr Java unterrichten werden.  Nun ja, die sind ja noch nicht rum…

Jedenfalls habe ich mich kürzlich etwas peinlich berührt an diese Aussage erinnert: Ich habe nämlich eine Fortbildung zu Java 8 geleitet.  Das war für mein Ziel, Java aus der Schule zu verbannen, wohl eher kontraproduktiv — diente aber auch einem anderen Zweck.  Ich wollte den Kollegen eher zeigen, dass Objektorientierung nicht alles ist, dass Java jetzt endlich auch funktionale Elemente hat und dass das Programmieren in Java damit sogar richtig Spaß machen kann.

Ich kann natürlich hier keine zweitägige Fortbildung reproduzieren, würde euch aber meine Materialien, ein paar verblüffende Beispielprogramme und eine Aktualisierung der Diskussion über Java in der Schule anbieten. Wer also schon länger wissen wollte, was es mit diesen Lambda-Ausdrücken, funktionalen Interfaces und Streams auf sich hat, und auch, ob das für die Schule relevant sein könnte, der möge, dadadam dadadam, für dieses Thema stimmen.


So, und nun hoffe ich auf rege Beteiligung.  Ich lasse die Abstimmung eine Woche laufen, ach, sagen wir doch gleich: bis Weihnachten 2015! Danach gebe ich das Ergebnis hier bekannt und setze mich in den Weihnachtsferien hin und schreibe.

Dadadam dadadam!

Informatik so unterrichten, wie ich es mir wünsche

(Ich blogge ja nicht mehr. Vive le blog!)

Direkt nach dem Ende des Referendariats hatte ich das große Glück, neben meinem „normalen“ Unterricht als AG-Leiter beim Freiburg-Seminar einsteigen zu können.  Dort können „begabte“ Jugendliche sich intensiv mit Themen aus Mathematik und Naturwissenschaften befassen, freiwillig und ohne Noten, dafür umso begeisterter.

Obwohl das Seminar offiziell nur Mathematik und Naturwissenschaften im Namen trägt, gibt es dort auch Informatik-Kurse.  (Was wären die MINT-Fächer denn ohne Vokal?) Und gerade die Informatik-AGs sind besonders begehrt; wir könnten, gerade für neugierige Anfänger, locker noch ein oder zwei Kurse mehr anbieten, wenn wir dürften.  Dieser Run auf die Informatik ist auch überhaupt kein Wunder — es ist ja nicht so, dass junge Leute nicht neugierig wären auf diese Wissenschaft, diese Technologie, die unser Welt in den letzten Jahrzehnten komplett verwandelt haben.  Nein, die Schüler spüren und wissen, dass Informatik wichtig und spannend ist — nur die baden-württembergische Bildungspolitik gibt ihnen kaum eine reguläre schulische Möglichkeit dazu, sich damit auseinanderzusetzen.

(Es besteht im Augenblick die leise Hoffnung, dass sich daran demnächst doch noch etwas ändert. Aber color me skeptical.)

Jedenfalls ist es für mich eine große Freude, diese jungen Leute, die durch irgendwelche glücklichen Zufälle den Weg zur Informatik gefunden haben, zu unterrichten. In meinem ersten Jahr (2013/14) war das Kursthema noch vom Vorgänger vorgegeben, also haben wir uns mit objektorientierter Programmierung beschäftigt, aber zum Glück ohne die Zwänge eines Lehrplans.  „Programmieren kreativ“ nannte ich den Kurs und es entstanden tolle Spiele und andere Anwendungen dabei.  Am Schluss sprachen wir ein wenig über KI, mein altes Forschungsgebiet, und es wurde beschlossen, das im darauffolgenden Jahr mal genauer anzuschauen…

Was dabei 2014/15 herauskam, könnt ihr in meinem Abschlussbericht nachlesen (vollständiger Text als PDF bei Klick aufs Bild):

Teaser

Ich glaube, dass ich in den letzten beiden Jahren riesengroßes Glück mit meinen Teilnehmern hatte (s. z.B. der letzte Absatz des Berichts).  Da der Kurs in dieser Form wegen des Abgangs der Abiturienten sowieso nicht mehr weiterbestehen wird, habe ich darum gebeten, dieses Jahr eine AG für die Unter- und Mittelstufe anbieten zu dürfen, „Informatik für Programmieranfänger“.

Warum dieser scheinbare Schritt zurück im Anspruchsniveau?

Ich gebe es offen zu: Ich will nicht nur die nerdigen Jungs, die über’s Minecraft-Modden oder die PHP- und JavaScript-Programmierung ihrer eigenen Webseite zur Informatik gekommen sind.  Ich will auch die anderen hellen Köpfe, die sonst immer in der Mathe-AG landen.  Ich will die Musischen, die den Computer als Medium für Kreativität entdeckt haben.  Ich will denen, die einfach Spaß am Knobeln und Problemlösen haben, den Computer als Medium fürs Denken schmackhaft machen.  Insbesondere und ganz explizit will ich versuchen,  Mädchen für das Fach zu begeistern.  Dafür gibt es tausend Gründe, die ich jetzt nicht aufzählen kann und will (ich hab ja keine Zeit mehr fürs Bloggen).

Jedenfalls kann ich mich nicht zurücklehnen und darauf hoffen, dass diese Gruppen sich in der Oberstufe plötzlich in meinen Kurs verirren — die muss man vorher abgreifen und auf den Geschmack bringen!

Deshalb ein Anfängerkurs. Mit Snap! und später vielleicht Python.  (Von mir aus könnte es auch das ganze Jahr Snap! bleiben. Dem Niveau sind in Snap! ja keine Grenzen gesetzt. Mal sehen, wie die Stimmung so ist.)  Bisher läuft es großartig — genau wie in meinen anderen, „etwas weniger begabten“ Klassen, bei denen ich am Anfang des Schuljahres auch immer mit Snap! starte:  Hohe Motivation, viel Spaß, verschachtelte Schleifen innerhalb der ersten 30 Minuten, das erste Spiel innerhalb einer Doppelstunde.  Frust kommt immer erst auf, wenn wir zu Java wechseln müssen.

Bin gespannt, was ich alles machen kann mit meinen schlauen 12-14-Jährigen… Ich freue mich total darauf!

Mehr Expressivität wagen

(Dieser Post schließt sich an den letzten zum Thema Snap! an.)qs

In der funktionalen Programmiersprache Haskell gibt es eine berühmte Implementierung des Quicksort-Algorithmus, die nur zwei (!) Zeilen lang ist. Wem Haskell zu exotisch ist, der kann die Idee z. B. in Python so umsetzen:

def qsort(list):
  if not list:
    return []
  else:
    pivot = list[0]
    less = [x for x in list[1:] if x < pivot]
    more = [x for x in list[1:] if x >= pivot]
    return qsort(less) + [pivot] + qsort(more)

In der Nacht vor dem Workshop in München fiel mir ein, dass man diese Implementierung fast direkt nach Snap! übertragen kann, nämlich so:qsHier kann ich leider nicht die schönen list comprehensions aus Haskell oder Python verwenden, sondern nutze die klassische higher-order Funktion filter (in Snap! heißt sie keep items such that). Filter wird mit einer Liste und einem Prädikat (= Funktion, die einen Wahrheitswert berechnet) als Argumenten aufgerufen und liefert eine neue Liste zurück, die nur diejenigen Werte enthält, für die das Prädikat wahr wird — es werden also nur diejenigen Listenelemente „gefiltert“, die eine Bedigung erfüllen. In unserem Fall ist die Bedingung bzw. das Prädikat ein kurzer Lambda-Ausdruck, der prüft, ob das jeweils an die Leerstelle* im grau umrandeten Puzzleteil eingesetzte Element der Liste kleiner als das Pivot-Element ist.

Mir ist schon klar, dass jemand, der sich noch nie mit funktionaler Programmierung beschäftigt hat, das nicht unbedingt sofort intuitiv finden wird — auch mir gefällt gerade deshalb die Python-Version noch besser, weil sie so nahe an der vertrauten mathematischen Mengennotation ist. Aber dass es sich auch beim Snap!-Code um eine ganz direkte Umsetzung des oben umgangssprachlich beschriebenen Prinzips handelt, sollte offensichtlich sein.

Wie wäre es in Java? Natürlich ebenso möglich, aber viel, viel umständlicher. Es fehlen einfach die mächtigeren Werkzeuge zur Manipulation von Listen. Dadurch wird die Umsetzung von Quicksort in Java zwangsweise auf einem wesentlich niedrigeren Abstraktionsniveau stattfinden müssen als in Haskell, Python und Snap!, wo die fünf Schritte des umgangssprachlichen Algorithmus direkt in Code übertragen werden können. Ich glaube, dass es für Schüler schon schwer genug ist, die Grundidee von Quicksort zu verstehen. Ihn bei der Umsetzung in ein Programm dann auch noch sofort „herunterbrechen“ zu müssen auf explizite Schleifen und Anweisungen zum Erstellen und Verändern von Listen, das kommt mir noch viel schwerer vor.

„Stopp mal!“ wird jetzt vielleicht mancher rufen. „Der Algorithmus, so wie du ihn präsentiert hast, ist aber furchtbar ineffizient. Die Liste wird da unnötigerweise zweimal durchlaufen (in beiden Aufrufen von keep items such that). Ich mache das alles in einer Schleife; geht viel schneller.“

Wer das ruft, hat sachlich recht — liegt aber meiner Meinung nach pädagogisch falsch. Schüler müssen zuerst sehen, wie ein Verfahren überhaupt in Code umgesetzt werden kann, und das möglichst high-level. Dann und erst dann sollen sie sich über die Effizienz Gedanken machen. Idealerweise führt die Suche nach einer effizienteren auch zu einer eleganteren Lösung. In Python z.B. sähe die erste Fassung mit nur einem Listendurchlauf vielleicht so aus:

def qsort(L):
  if not L:
    return []
  pivot = L[0]
  less, more = [], []
  for x in L[1:]:
    if x < pivot:
      less.append(x)
    else:
      more.append(x)
  return qsort(less) + [pivot] + qsort(more)

Die Liste wird also nun mittels einer Schleife in zwei neue Listen partitioniert. Aber der Code ist (finde ich) viel schwerer verständlich geworden. Und ist es für den Algorithmus denn wichtig, auf welche Weise die Partitionierung durchgeführt wird? Natürlich nicht! Verstecken wir die Details also in einer Funktion:

def partition(L, pivot):
    less, more = [], []
    for x in L:
        if x < pivot:
            less.append(x)
        else:
            more.append(x)
    return less, more

def qsort(L):
    if not L:
        return []
    pivot = L[0]
    less, more = partition(L[1:], pivot)
    return qsort(less) + [pivot] + qsort(more)

So, jetzt sieht qsort richtig aufgeräumt aus; sogar kürzer als die ursprüngliche Fassung mit den zwei list comprehensions.  Die spezielle Aufgabe der Partitionierung wurde schön isoliert und in eine eigene Funktion partition ausgelagert. Und wenn man sich das so ansieht, kommt plötzlich eine entscheidende Erkenntnis: So etwas wie partition kann man eigentlich öfter brauchen, z.B. wenn man gerade und ungerade Zahlen von einander trennen will oder Worte, die mit Großbuchstaben beginnen, von denen, die es nicht tun, oder Datensätze von weiblichen und männlichen Mitarbeitern. Man könnte dafür eine Reihe von Funktionen schreiben, die jedesmal eine gegebene Liste in zwei neue aufteilt. Diese Funktionen glichen sich komplett — nur der Test zur Entscheidung, wohin ein Listenelement sortiert wird, wäre immer ein anderer.

„Lauter Funktionen, die fast gleich sind… Kann man da nicht etwas zusammenfassen?“ fragt sich da vielleicht der eine oder andere aufgeweckte Oberstufenschüler, der Ähnliches schon bei der Einführung von Funktionen, deren Parametrisierung, der Vererbung, womöglich dem einen oder anderen Design Pattern oder allgemein dem DRY-Prinzip gesehen hat. Und wir antworten: „Aber ja doch — Higher-order functions to the rescue!“

def partition(L, fn):
    falses, trues = [], []
    for x in L:
        if fn(x):
            falses.append(x)
        else:
            trues.append(x)
    return falses, trues

def qsort(L):
    if not L:
        return []
    pivot = L[0]
    def ist_kleiner(x):
        return x < pivot
    less, more = partition(L[1:], ist_kleiner)
    return qsort(less) + [pivot] + qsort(more)

Hier gibt’s nun natürlich einiges zu sehen: Funktionen als Parameter von Funktionen (hier: fn).  Eine Funktion (ist_kleiner), die innerhalb einer anderen Funktion (qsort) definiert wird und dabei Bezug nimmt auf eine Variable außerhalb ihrer eigenen Definition (pivot).*** Trotzdem: Ich glaube, wenn man — anders als wir mit C, Pascal, Java groß Gewordenen — noch gar nicht verinnerlicht hat, dass Funktionen keine Variablenwerte sein dürfen, tut man sich damit gar nicht so schwer.

Diese Version des Algorithmus gefällt mir nun sehr gut: Wir haben einerseits die Effizienz eines einzigen Listendurchlaufs pro Rekursionsionschritt, andererseits haben wir eine neue, vielseitig einsetzbare Funktion partition und wieder eine kurze, knackige Funktion qsort, die nur das enthält, was wirklich nötig ist.

Noch kürzer-knackiger, aber unnötig verwirrend für Schüler wäre qsort mit einem Lambda-Ausdruck, d.h. einer Funktion, die so klein und unwichtig ist, dass sie nicht einmal einen Namen bekommt:

def partition(L, fn):
    falses, trues = [], []
    for x in L:
        if fn(x):
            falses.append(x)
        else:
            trues.append(x)
    return falses, trues

def qsort(L):
    if not L:
        return []
    pivot = L[0]
    less, more = partition(L[1:], lambda x: x < pivot)
    return qsort(less) + [pivot] + qsort(more)

Egal in welcher Version, wir haben nun ein Programm, dass nicht nur effizienter ist als das Ursprüngliche, sondern auch modularer, flexibler, allgemeiner****.  Wenn man dahin kommen könnte, dass Schüler verstehen, warum so etwas gut und erstrebenswert ist…

Nur der Vollständigkeit halber kommt jetzt noch eine eher funktionale Version von partition, ohne Schleife, dafür mit Rekursion.  Kann man so machen, muss man aber überhaupt nicht. Lehrreich — und irgendwie schön — ist diese Version aber vielleicht doch.

def partition(L, fn):
    if not L:
        return [], []
    first, rest = L[0], L[1:]
    falses, trues = partition(rest, fn)
    if fn(first):
        return [first] + falses, trues
    else:
        return falses, [first] + trues

Nun aber zum Titel dieses Artikels: Ich weiß, dass Kollegen, die das Glück haben, einen vierstündigen Informatikkurs in der Oberstufe anbieten zu können, durchaus auf diesem Niveau unterrichten — auch in Java. Ich bin in einer anderen Situation: Meine Oberstufenschüler haben in ihrer gesamten Schulkarriere nur zweimal ein halbes Schuljahr zwei Wochenstunden lang Gelegenheit zu erfahren, was Programmieren überhaupt ist. Das macht insgesamt 80 Schulstunden in ihren letzten beiden Schuljahren, in einem Fach, das für diese Schüler im Abitur minimale Relevanz hat. Für mich ist also jede Minute kostbar, in der ich diesen Schülern ein bisschen das informatische, problemlösende, abstrahierende Denken vermitteln kann.  Jedesmal, wenn ich erklären muss, was void oder static oder public oder float oder int bedeutet (oder den Schülern vermittle, dass sie das zwar dauernd tippen, aber sowieso nicht verstehen müssen/können), verlieren ich und die Schüler Zeit, die sie damit verbringen könnten, an der Lösung eines Problems zu knobeln. Bei so wenig Zeit wünsche ich mir, eine Sprache benutzen zu können, die maximal anfängerfreundlich ist (so wenig und so intuitive Syntax wie möglich) und gleichzeit so ausdrucksstark, dass sie die Schüler nicht beim Denken behindert!

Ich bin ich mir noch nicht sicher, ob Snap! diese Sprache ist. Andererseits: Was für die Studenten der UC Berkeley hervorragend funktioniert, sollte eigentlich auch für deutsche Schüler gut genug sein, oder?

P.S. Warum habe ich eigentlich in meinem Snap!-Quicksort das Pivot-Element zufällig ausgewählt? Wer das weiß, bekommt nen Ball!

P.P.S. Man kann ja in Snap! eigene Kontrollstrukturen programmieren (s. Bild im letzten Post).  Wenn das geht, dann kann man sich doch sicher auch eigene list comprehensions bauen. Vorschläge, anybody?

** Ist diese Leerstelle nicht visuell wunderbar anschaulich dafür, dass dieser Block noch nicht ausgeführt werden kann, weil ihr das Entscheidende, ein Argument, noch fehlt?

*** Übrigens will ich hier gar nicht so tun, als könnte man Ähnliches nicht auch in Java tun.  „Anonyme innere Klassen“ heißt hier das Stichwort. Als ich die vor über 10 Jahren kennenlernte und verstand, was ich in Java damit anfangen kann, war ich sowas von begeistert — und bin tatsächlich, glaube ich, dadurch ein besserer Programmierer geworden. Heute kenne ich aber auch ein paar Sprachen, in denen die zugrundeliegende Idee (closures) so viel einfacher umgesetzt werden kann (z.B. in unserem Python-Beispiel die Funktion ist_kleiner) als in Java.

**** Eigentlich sollte man sollte man qsort nun aber noch mit der Vergleichsfunktion parametrisieren, so dass man z.B. mit qsort(L, ist_groesser) absteigend sortieren könnte.  Aber das überlasse ich als Hausaufgabe euch Lesern.

Snap!

Letzte Vorletzte Woche Vor drei Wochen habe ich in München einen Workshop zur graphischen Programmiersprache Snap! gehalten, beim Tag der Informatiklehrerinnen und Lehrer, organisiert u.a. vom geschätzten Kollegen Herrn Rau.

„In zwei Stunden von der Grundschule ins Informatikstudium“ war das Motto, d.h. es ging mir darum, den Teilnehmern in kurzer Zeit einen Einblick über die das breite Spektrum von Themen zu geben, die man mit Snap! abdecken kann — angefangen mit dem Zeichnen einfacher geometrischer Figuren (à la LOGO und Scratch) bis hin zu Lambda-Ausdrücken und dem Programmieren eigener Kontrollstrukturen*.  indexIch bin v. a. deshalb von Snap! so angetan, weil man auch komplexe Konzepte wie Rekursion oder first-class Funktionen ganz ohne Syntaxbarriere vermitteln kann.

Wer schon einmal mit Scratch gearbeitet hat, weiß, wie sehr das „Zusammenpuzzeln“ von Programmen die Schüler zum schnellen Experimentieren einlädt. Snap! geht in dieser Hinsicht nochmal einen kleinen Schritt weiter, denn um losprogrammieren zu können, muss nun nicht einmal mehr eine Software installiert werden:   Snap! läuft komplett im Browser, d.h. man braucht als Benutzer nichts zu installieren, sondern geht einfach auf http://snap.berkeley.edu/snapsource/snap.html (oder sucht im Web nach „run snap“) und legt los.

Selber loslegen sollten auch die Teilnehmer des Workshops; deswegen habe ich versucht, ihn so interaktiv wie möglich zu gestalten.  Das kann ich in einem Blogartikel so natürlich nicht machen.  Ich habe aber hinterher meine Präsentation so überarbeitet, dass sie — hoffentlich — auch für sich allein einigermaßen lesbar ist.  V.a. enthält sie nun ganz viele Links zu meinen Beispielprogrammen in der Snap!-Cloud. Einfach einen Programmlink anklicken, Programm ausprobieren und dann das Symbol mit den beiden Pfeilen drücken, um den Source-Code anschauen.

Zur Präsentation

Zum Beispiel findet ihr dort diesen hübschen Baum und diese Animation (bitte Maus im Fenster bewegen). Und noch vieles mehr… also viel Spaß bei Lesen und Mitprogrammieren!

In der Nacht vor dem Workshop fiel mir noch ein schönes Beispiel ein… was dazu führte, dass ich mich um 2 Uhr früh in München nochmal an einen fremden Rechner gesetzt und programmiert habe. (Dabei war es natürlich ganz großartig, dass ich weder die Software noch meinen eigenen Code auf dem Laptop oder einem Stick dabeihaben musste.) Was mir da einfiel, war der Quicksort-Algorithmus, das klassische Beispiel für ein Divide-and-Conquer-Verfahren.**

qs

Wer mag, darf sich als Hausaufgabe gern selbst an der Umsetzung in Snap! oder der Programmiersprache seiner Wahl versuchen.  Eine (erste) Snap!-Lösung findet ihr in der Präsentation.  Ich werde das Quicksort-Beispiel noch sehr ausführlich in einem weiteren Artikel diskutieren, in dem es um die Expressivität von Programmiersprachen für die Schule geht.

Für heute soll’s hiermit aber gut sein! Ich weiß selbst noch überhaupt nicht, ob und wie ich Snap! weiter einsetzen werde.  Vieles daran ist orthogonal zu den Lehrplänen, nach denen ich unterrichten werde, und vielleicht auch manchmal zu dem, was Schüler und spätere Arbeitgeber von Informatikunterricht erwarten.

Was denkt ihr? Wie findet ihr Snap!? Würdet ihr es im Unterricht einsetzen?*** Ich freue mich sehr über (positive wie kritische) Rückmeldungen zu Snap!, dem Workshop und den Materialien.

* Versucht das mal in Java!

** Wir haben das damals noch im Grundkurs Informatik, Klasse 13, nach 2 Jahren Informatikunterricht gelernt. Kommt mir heute durchaus anspruchsvoll vor. Und ist natürlich auch so gar nicht objektorientiert…

*** Das Thema „Snap! und Objektorientierung“ haben wir im Workshop diskutiert. Hier nur ganz kurz: In der aktuellen Version von Snap! kann man durchaus objektorientiert programmieren, es ist aber alles andere als natürlich im Vergleich zu Sprachen, die explizit für OOP entworfen wurden.  Dass es überhaupt geht, ist ein Beweis für die Ausdrucksstärke der Sprache (Zitat aus dem Reference Manual: „Snap! is Scheme disguised as Scratch“).  Ich kann überhaupt die Lektüre dieses Manuals nur empfehlen.  Ihr werdet mit den Ohren schlackern!

Musik ist hier Programm! (2)

(Dies ist Teil 2 der Serie „Musik ist hier Programm!“  – aber der erste mit richtig Musik und Code.  Die Einleitung gibt’s hier.)

Wenn Musikexperten über Musik (egal ob Klassik, Jazz oder Pop) sprechen, klingt das häufig ziemlich mathematisch: „Die 5. Stufe einer Moll-Tonleiter entspricht der 3. Stufe der parallelen Dur-Tonleiter“, „der C7/9/13-Akkord“, „die II-V-I-Verbindung“ (hier sogar mit römischen Zahlen!) usw.  Das liegt daran, dass Musik auf eine absolut faszinierende Weise kreative Freiheit mit mathematischer Regelhaftigkeit verbindet — und gerade in diesem scheinbaren Widerspruch liegt ein großer Reiz.

Auch wir fangen mathematisch an, nämlich indem wir die Halbtonschritte, aus denen die chromatische Tonleiter besteht, durchnummerieren.  Also: c=0, cis=1, d=2, dis=3, …, h=11, c (eine Oktave höher) = 12, usw.   Genauso macht das auch das MIDI-Protokoll (übersichtlich an einer Klaviatur dargestellt z.B. hier).

In Python können wir damit eine chromatische Tonleiter, d.h. 12 aufeinanderfolgende Halbtonschritte, ganz einfach so beschreiben:

chromatic_scale = range(13)

Mein Vorschlag wäre übrigens, dass ihr den interaktiven Python-Interpreter öffnet und die Beispiele direkt dorthin kopiert und ausführt.  Am Ende dieses Artikels findet ihr alle Definitionen auch nochmal am Stück.

Euer „Dialog“ mit dem interaktiven Interpreter könnte also ungefähr so aussehen (die letzte Zeile stellt die Ausgabe des Interpreters dar):

chromatic_scale = range(13)
list(chromatic_scale)
==> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

Für eine direkte MIDI-Ausgabe wäre das ja gar nicht schlecht… aber leider kann Lilypond mit Zahlen alleine noch nichts anfangen. Wir müssen unsere Zahlen also vor der Ausgabe in Notennamen umwandeln. Wie das Format für Notennamen von LilyPond aussieht, steht hier.

Für den Anfang bestimmen wir Notennamen und Oktavangabe mit der folgenden einfachen Funktion:

def midi2lily(pitch):
    names = ["c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b"]
    octave, semi = divmod(pitch, 12)
    octave_marker = "'" if octave > 0 else ","
    return names[semi] + abs(octave)*octave_marker

Mit einer List Comprehension können wir diese Funktion auf die gesamte chromatische Tonleiter anwenden:

[midi2lily(pitch) for pitch in chromatic_scale]
==> ['c', 'cis', 'd', 'dis', 'e', 'f', 'fis', 'g', 'gis', 'a', 'ais', 'b', "c'"]

Schön, schön. Bei der letzten Note sieht man auch, dass noch ein Apostroph angehängt wurde, weil dort eine neue Oktave beginnt. Die Funktion scheint also zu tun, was sie soll.

(Aber Achtung: Diese Funktion ist musikalisch oft nicht sinnvoll… z.B. sollte in F-Dur nicht ‚ais‘ verwendet werden, sondern ‚bes‘ (LilyPond-Notation für die Note „B“), damit nicht ein Ais (A mit Kreuzzeichen), sondern B (H mit B-Vorzeichen) geschrieben wird. Darum kümmern wir uns in der nächsten Folge.)

Indem wir nur bestimmte Töne aus der chromatischen Skala auswählen, können wir andere Tonleitern erzeugen. Das gehen wir bald systematisch an – für den Anfang können wir aber z.B. eine Ganztonleiter produzieren, indem wir nur jeden zweiten Halbton verwenden:

[midi2lily(pitch) for pitch in chromatic_scale if pitch % 2 == 0]
==> ['c', 'd', 'e', 'fis', 'gis', 'ais', "c'"]

Das sieht ja schon ganz gut aus!  Ein zweites Beispiel (unter Verwendung von itertools.chain):

from itertools import chain
motiv1 = [pitch for pitch in chromatic_scale if pitch % 2 == 0]
motiv2 = reversed(motiv1)
theme = chain(motiv1, motiv2)

[midi2lily(pitch) for pitch in theme]
==> ['c', 'd', 'e', 'fis', 'gis', 'ais', "c'", "c'", 'ais', 'gis', 'fis', 'e', 'd', 'c']

Ah, schön: Wir haben ein musikalisches Thema beschrieben, das aus einer aufsteigenden, gefolgt von einer absteigenden Ganztonleiter besteht.  Interessant daran ist v.a. die Art, wie wir das in Python ausgedrückt haben.  Das Thema besteht aus zwei Motiven, nämlich zuerst der Ganztonleiter und dann einer Wiederholung des ersten Motivs — aber rückwärts gespielt!  In der Musik nennt man so etwas „Krebs“. Indem wir das zweite Motiv mittels reversed(motiv1) definieren, können wir diese Krebs-Beziehung direkt und kompakt beschreiben.  Wir bewegen uns im Programm also auf einer viel höheren Abstraktionsebene als im späteren Notentext, wo der Zusammenhang zwischen den Motiven komplett verlorengeht.  Auch wenn das motiv1 ein anderes wäre —  motiv2 wäre immer dessen Krebs, denn wir haben nicht einzelne Noten definiert, sondern genau diese Beziehung zwischen den beiden Motiven.  Sehr spannend — und wir werden das noch viel weiter treiben!

Bisher erzeugen wir nur Python-Listen, die Notennamen enthalten.  Natürlich muss aus einer solchen Liste irgendwann ein String werden, der in einer Lilypond-Datei verwendet werden kann. Definieren wir uns also gleich mal:

def join(it):
    return " ".join(it)

Die folgende Zeile z.B. nimmt also jedes zweite Element der chromatischen Tonleiter, übersetzt es in einen Notennamen und fügt alle diese Namen zu einem String zusammen:

join(midi2lily(p) for p in chromatic_scale if p % 2 == 0)
==> "c d e fis gis ais c'"

Übrigens: Wer sich jetzt wundert, dass hier keine eckigen Klammern mehr vorkommen, d.h. die List Comprehension aus dem vorigen Beispiel irgendwie verschwunden ist, der hat gut beobachtet! join() wird hier mit einer „generator comprehension“ aufgerufen, d.h. es wird keine Liste, sondern ein Generator erzeugt, dessen konkrete Werte erst berechnet werden, wenn wirklich auf die einzelnen Elemente zugegriffen werden soll. Wer mehr über List Comprehensions und Generator Comprehensions wissen will, kann sich
ja mal diesen Artikel von Guido van Rossum himself anschauen oder auch diesen.

Bevor wir nun aber über Tonleitern und Akkorde nachdenken, sollten wir zuerst noch die Infrastruktur schaffen, um wirklich mit Lilypond Noten darstellen und sie als MIDI-Datei anhören zu können.  Die Ganztonleiter von gerade eben beispielsweise, müsste zumindest in die folgende Lilypond-Datei „verpackt“ werden:

\version "2.16.2"    % compatibility information for future LilyPond versions

\score {
{ c d e fis gis ais c'}

\layout { }

\midi { }
}

Diese Datei kann LilyPond dann verarbeiten und eine PDF-Datei erzeugen, die so aussieht:minimal-ausschnittHier ist vielleicht noch nicht alles, wie wir es uns vorstellen (wollten wir wirklich so tiefe Töne?), aber ein Anfang ist es allemal.

Wie bekommen wir unsere Daten also in das LilyPond-Format?  Ich benutze für so etwas gerne Pythons template strings. Damit bauen wir die Lilypond-Datei einfach nach, verwenden aber an den enscheidenden Stellen Platzhalter (hier z.B. $melody und $speed):

from string import Template

SIMPLE_LP_MELODY = """
\\version "2.16.2"   % compatibility information for future LilyPond versions

\\score {
{ $melody }

\\layout { }
\\midi { \\tempo 2 = $speed}
}
"""

Wir können das z.B. so benutzen:

>>> adict = dict(melody="c d e fis gis ais c'", speed=120)
>>> text = Template(SIMPLE_LP_MELODY).substitute(adict)
>>> print(text)

Auf Dauer noch hilfreicher wird das, wenn die Ersetzungen selbst wieder Platzhalter enthalten dürfen und wir dann (quasi rekursiv) so lange ersetzen, bis ein String ohne Platzhalter entstanden ist.  Das machen wir mit folgender Funktion:

def fill_template(tmpl, adict):
    previous = ""
    while tmpl != previous:
        previous = tmpl
        tmpl = Template(tmpl).safe_substitute(adict)
    return tmpl

Damit könnten wir im interaktiven Python Interpreter jetzt z.B. definieren:

motiv1 = [pitch for pitch in chromatic_scale if pitch % 2 == 0]
motiv2 = reversed(motiv1)
theme = chain(motiv1, motiv2)
melody = join(midi2lily(p) for p in theme)
speed = 120
text = fill_template(SIMPLE_LP_MELODY, locals())
print(text)

und erhalten als Ausgabe tatsächlich eine komplette LilyPond-Beschreibung:


\version "2.16.2"   % compatibility information for future LilyPond versions

\score {
{ c d e fis gis ais c' c' ais gis fis e d c }

\layout { }
\midi { \tempo 4 = 120}
}

Der obige Code enthält einen praktischen Python-Trick (an der Grenze zum Hack): Die Funktion locals() liefert ein Dictionary mit den lokal definierten Namen und ihren aktuellen Werten.  Wenn ich z.B. locals() in einer Funktion verwende, sind das alle dort definierten Variablen und die Aufrufparameter.  Im Klartext: Die Werte für die in SIMPLE_LP_MELODY verwendeten Platzhalter werden direkt aus den lokalen Variablen ermittelt, ohne dass wir dazu ein anderes Dictionary bauen müssen.  Wem das zu nahe an schwarzer Magie ist, der schreibt:

text = fill_template(SIMPLE_LP_MELODY, dict(melody=melody, speed=speed))

Egal in welcher Variante, die Variable text enthält nun eine sinnvolle Eingabe für LilyPond.  Also schnell in eine Datei damit:

def save_file(astring, fn="output.ly"):
    out_file = open(fn, "wt")
    out_file.write(astring)
    out_file.close()

save_file(text)

Damit wird der Text standardmäßig die Datei output.ly geschrieben.  Wenn wir LilyPond darauf loslassen (unter Windows z.B. so), erzeugt es für uns Noten (output.pdf)

ganztonund anhörbare Musik in output.midi.  Hat’s geklappt?  Die MIDI-Datei kann z.B. euer Webbrowser abspielen (mein Firefox zumindest kann es).  Leider kann ich sie aber weder bei wordpress.com noch bei soundcloud hochladen. Damit ihr unser erstes Musikstück anhören könnt, habe ich output.midi deshalb nach output.mp3 konvertiert:

Juhu, wir haben Musik produziert! Keine besonders schöne, zugegeben.  Ganztonleitern klingen irgendwie… seltsam.  Deswegen werden wir uns als Nächstes mit Tonleitern beschäftigen, die uns vertrauter sind: Dur, Moll, aber auch exotisch Klingenderes wie Phrygisch oder Mixolydisch (das ist z.B. die Tonart von Norwegian Wood) sind nur noch wenige Programmzeilen entfernt.

Aber das machen wir beim nächsten Mal.  Dieser Beitrag ist lange genug geworden und bietet hoffentlich schon einige Ansatzpunkte zum Experimentieren und Weiterdenken.  Da ich wegen der anstehenden Prüfungen hier mindestens zwei Wochen nicht weiterschreiben kann, könnt ihr euch ja gerne selbst schon mal überlegen, wie ihr  Tonarten modellieren würdet.  Es gibt unendlich viele Wege das zu tun.

Viel Spaß beim Ausprobieren! Über Kommentare, Fragen und Anregungen freue ich mich sehr.

Hier noch mal der vollständige Code, den wir heute entwickelt haben, inklusive des letzten Beispiels.  Diesen Code kann man so als Datei speichern und von Python3 ausführen lassen:

from string import Template

def midi2lily(pitch):
    names = ["c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b"]
    octave, semi = divmod(pitch, 12)
    octave_marker = "'" if octave > 0 else ","
    return names[semi] + abs(octave)*octave_marker

def join(it):
    return " ".join(it)

SIMPLE_LP_MELODY = """
\\version "2.16.2"   % compatibility information for future LilyPond versions

\\score {
  { $melody }

  \\layout { }
  \\midi { \\tempo 2 = $speed}
}
"""

def fill_template(tmpl, adict):
    previous = ""
    while tmpl != previous:
        previous = tmpl
        tmpl = Template(tmpl).safe_substitute(adict)
    return tmpl

def save_file(astring, fn="output.ly"):
    out_file = open(fn, "wt")
    out_file.write(astring)
    out_file.close()

## Beispiel
from itertools import chain

chromatic_scale = range(13)
motiv1 = [pitch for pitch in chromatic_scale if pitch % 2 == 0]
motiv2 = reversed(motiv1)
theme = chain(motiv1, motiv2)
melody = join(midi2lily(pitch) for pitch in theme)
speed = 120
text = fill_template(SIMPLE_LP_MELODY, locals())
save_file(text)

Musik ist hier Programm! (1)

(Alternativer Titel: „Musik machen mit Embee“ bzw.  „Komponieren durch Programmieren“  bzw. „Hilfe! Mein Computer will Musiker werden!“ bzw. „Harmonielehre am Computer“ bzw. „Vom Personal Computer zum Personal Composer“.  Sucht euch einen aus.)

Dies ist der Beginn einer Serie, in der ich mit euch Musik machen will.  Beziehungsweise: Eigentlich will ich, dass mein (bzw. euer) Computer die Musik macht — und wir bringen ihm bei, wie. Er soll lernen, was Töne, Tonleitern, Akkorde und Akkordverbindungen sind und er soll dieses Wissen nutzen um Musikstücke in Noten zu setzen, sie zu verändern, sie zu begleiten und — als Krönung — sie selbst zu komponieren.  Ob das mehr wird als Kakophonie, wird sich zeigen!

Bevor es losgeht, will ich ein paar Fragen beantworten (die mir natürlich niemand gestellt, sondern die ich mir selbst ausgedacht habe):

Für wen ist das hier gedacht?

Die Reihe richtet sich an alle, die sich für Musik und für Programmierung interessieren. Auf beiden Gebieten setze ich Grundkenntnisse voraus; weder soll das eine Einführung in die Musiktheorie noch ein Programmierkurs werden.  Ich gehe aber kleinschrittig und hoffentlich so anschaulich vor, dass es Spaß macht, mitzulesen, mitzudenken, die Musikbeispiele anzuhören, die Notenbeispiele anzusehen, v. a. aber die Programme selbst auszuprobieren und mit ihnen zu spielen.  Links zu ein- und weiterführenden Informationen versuche ich, wo immer möglich, einzubinden.

Warum sollte man sowas überhaupt wollen, ein Computerprogramm das Musik macht?

Mir persönlich würde als Antwort auf diese Frage ja schon ausreichen: Aus Neugierde! Um zu sehen, ob es möglich ist!  Um etwas zu lernen über Musik und über Programmierung!

Aber viele Leute fühlen sich von der Idee, mit dem Computer irgendeine Form von Kunst zu machen, richtiggehend abgestoßen.  Das äußert sich dann ungefähr so:

„Oh nein, was soll den der Mist? Ist Musik (und eigentlich alle Kunst) nicht in erster Linie Intuition, Emotion, Kreativität — und damit einem Computer gar nicht zugänglich?  Muss man denn alles Schöne durch Analyse, durch Automatisierung, durch Maschinen seines Zaubers berauben? Ich fand schon Gedichtinterpretationen in der Schule immer irgendwie pervers…“

Ich halte die Sache für etwas komplexer — und dadurch für viel spannender.  Aber den langen Beitrag, den ich dazu in der Schublade habe, lasse ich erstmal liegen.  Vielleicht können wir das ja in einiger Zeit diskutieren, wenn wir sehen, was wir überhaupt zustande bekommen.

Wird das etwas für die Schule?

Wohl eher nicht.  Die Programme, die wir entwickeln, werden nicht besonders umfangreich, aber doch anspruchsvoll werden. Für meine Schüler wäre das Niveau definitiv zu hoch; sie haben leider viel zu wenig Informatikunterricht.  Aber wer weiß? Vielleicht kann man mit Anpassungen in anderen Bundesländern, wo mehr Informatik möglich ist, durchaus was machen…  Fächerübergreifend… Ein Traum…

Welche Programmiersprache und Tools verwendest du?

Programmiert wird in Python.  Output unserer Programme werden Dateien sein, die die freie Notensatzsoftware LilyPond dann für uns in wunderschöne Noten übersetzt.  Weil Musik aber in erster Linie zum Hören da ist, erzeugen wir auch MIDI-Dateien (am Anfang lassen wir das einfach LilyPond für uns machen; später brauchen wir ein bisschen mehr Freiheit und machen es selbst).

Warum Python?

Python ist toll! Python-Code sieht aus wie Pseudocode und ist deshalb selbst für Leute nachvollziehbar, die selbst nicht in Python programmieren.  Python ist einfach, wenn etwas Einfaches ausgedrückt werden soll (im Gegensatz zu, sagen wir, Java) und macht einem auch bei komplexeren Aufgaben das Leben nicht unnötig schwer.

Wichtig für mich in diesem Projekt ist v. a. auch, dass Python eine funktionalen Programmierstil unterstützt: Funktionen können Parameter oder auch Rückgabewert anderer Funktionen sein — und das macht beim Beschreiben musikalischer Zusammenhänge sehr viel Sinn.  Z.B. werden wir die Tonika der Es-Moll-Tonleiter (es, ges, b) als Verknüpfung dreier Funktionen (Tonika, Es, Moll) beschreiben können.  Das wird sehr elegant, glaubt mir.  Im Vergleich zu ausschließlich funktionalen Sprachen (Lisp, Haskell, ML, …) bleiben wir mit Python aber immer im für Normalsterbliche nachvollziehbaren Rahmen.

Übrigens werden wir Python 3 verwenden.  Es gibt keinen zwingenden Grund dafür und die Codebeispiele sollten größtenteils auch unter Python 2 funktionieren bzw. sich leicht anpassen lassen.  Für mich war dieses Projekt aber der Anlass, endlich zu Python 3 zu wechseln.

Warum LilyPond?

Weil man Musik für LilyPond in einem textbasierten Format beschreibt, das relativ leicht programmatisch zu erzeugen ist.  (Aber auch als Mensch finde ich LilyPond zur Noteneingabe super.)

Geht’s jetzt mal endlich los?

Jawoll. Aber in einem neuen Eintrag.  Viel Spaß! Ich freue mich sehr über Kommentare, Fragen, Vorschläge…