=Paper= {{Paper |id=Vol-1496/paper7 |storemode=property |title=Zur Einschätzung von Programmierfähigkeiten - Jedem Programmieranfänger über die Schultern schauen |pdfUrl=https://ceur-ws.org/Vol-1496/paper7.pdf |volume=Vol-1496 |dblpUrl=https://dblp.org/rec/conf/abp/SchultenH15 }} ==Zur Einschätzung von Programmierfähigkeiten - Jedem Programmieranfänger über die Schultern schauen== https://ceur-ws.org/Vol-1496/paper7.pdf
Zur Einschätzung von Programmierfähigkeiten —
Jedem Programmieranfänger über die Schultern schauen

Bastian Schulten1, Frank Höppner2



Abstract: Am besten kann man den Kenntnisstand und die Fähigkeiten eines Studierenden in ei-
ner Programmierveranstaltung einschätzen, wenn man ihm während des Programmierens über die
Schulter schaut. Man erkennt, ob systematisch vorgegangen wird, welche Sprachkonstrukte sicher
sitzen oder nicht, wieviel Trial & Error im Spiel ist, etc. Da die Anzahl der Teilnehmer die der Be-
treuer aber um ein Vielfaches übersteigt, ist eine solche direkte Beobachtung schwer möglich. Wir
beschreiben vorbereitende Arbeiten und erste Teilergebnisse für die Erprobung eines Werkzeugs,
das eine nachträgliche Analyse der Entstehung einer Lösung erlaubt, um über (manuelle oder semi-
automatische) Auswertungen Rückschlüsse auf Wissenslücken und damit hilfreiche Aufgaben oder
sinnvolle Stoff-Wiederholungen zu erlangen.



1    Einleitung

Die Bewertung von Programmierfähigkeiten ist mühsam und aufwendig, weil eine Viel-
zahl von Aspekten zu berücksichtigen ist. Als harte Fakten muss das Endprodukt natürlich
die geforderte Funktionalität erfüllen, was durch Unit-Tests sehr gut bewerkstelligt wer-
den kann. Die Funktionalität mag als alleiniges Kriterium einer finalen Abgabe oder eines
Abschlussprojektes geeignet sein, aber im Laufe des Semesters – in der Zeit, in der ein
Studierender noch lernt, noch Fehler machen darf – ist es möglicherweise nicht das einzi-
ge und informativste Kriterium.

Gerade bei großen Studierendengruppen wird oftmals eine Bearbeitung von Aufgaben in
Zweiergruppen angeboten. Um die Relevanz der Bearbeitung von Aufgaben zu betonen,
werden Programmieraufgaben oft als Teilleistungen ausgeschrieben. Das kann wiederum
schwächere Studierende dazu verleiten, sich stärkeren anzuschließen, um sich die Teilleis-
tungen auf jeden Fall zu sichern. Im schlimmsten Fall führt das dazu, dass die schwächeren
Studierenden die Erstellung der Aufgaben weitestmöglich anderen überlassen, um kein Ri-
siko einzugehen. Dabei berauben sie sich selbst der wichtigen Praxis.
Kriterien für die Abnahme einer Aufgabe sind oft Funktionalität und Form, wie es bspw.
durch JUnit und Checkstyle automatisiert geprüft werden kann. So hilfreich diese Werk-
zeuge sind, sie tragen bewusst auch zu einer Begradigung oder Normierung der Lösungen
bei, so dass sich die Abgaben mehr ähneln, als es der Kenntnisstand der Studierenden ver-
muten ließe: Ob die Lösung in einem Zug runterprogrammiert oder das Ende einer langen
Trial & Error Odyssee darstellt, kann man dem Quelltext am Ende meist nicht mehr anse-
hen. Wenn ein Kommilitone zum Schluss geholfen hat, helfen danach weder Checkstyle
1 Ostfalia Hochschule, Fakultät Informatik, Am Exer 2, Wolfenbüttel, b.schulten@ostfalia.de
2 Ostfalia Hochschule, Fakultät Informatik, Am Exer 2, Wolfenbüttel, f.hoeppner@ostfalia.de
noch JUnit dabei, den Studierenden das nötige Feedback über den eigenen Leistungsstand
zurückzuspiegeln.

Unsere Hypothese ist, dass man den Kenntnisstand eines Studierenden am besten beurtei-
len kann, indem man ihm zuschaut: Wie geht er die Lösung an? Hat er einen Plan oder
hangelt er sich von Fehlermeldung zu Fehlermeldung? Folgt er blind den Vorschlägen
der Entwicklungsumgebung? Wählt er eine sinnvolle Reihenfolge der Lösung von Teil-
problemen? Diese Beobachtung verbietet sich im Regelfall aus Aufwandsgründen (ab-
gesehen von Prüfungssituationen). Diese Arbeit ist ein erster, vorläufiger Schritt hin zu
einem toolgestützten über die Schulter schauen. Die Grundidee ist die Aufzeichnung der
Entstehungsgeschichte eines Programms, so dass eine nachgelagerte Beurteilung der Her-
angehensweise möglich ist.


2     Verwandte Arbeiten

2.1    Evolution eines (Quell-) Textes

Angeregt zu diesem Ansatz haben Arbeiten über die Beurteilung der Fähigkeiten von an-
gehenden Schriftstellern: Gab es einen Entwurf? Wieviele Überarbeitungen gab es? Wurde
der Text in einem Stück geschrieben oder musste ständig vor und zurückgesprungen wer-
den? (Vgl. [Ma87, PR96]) Als Unterstützung für eine solche Beurteilung wurden in diesen
Disziplinen diverse Editoren geschaffen, die jeden Tastendruck bei der Entstehung eines
Aufsatzes dokumentieren (zum Beispiel3 ScriptLog), um diese Fragen im Nachhinein zur
Beurteilung heranziehen zu können. In den letzten Jahren gab es aber auch im Software-
Engineering verstärkt Bestrebungen, die Zwischenstände aus Versionsverwaltungen (wie
SVN, CVS) automatisch zu analysieren, um den wahren Umfang von Änderungen in
Code-Repositories abzuschätzen [CCP07], Bug-Tracking Einträge und Change-Log Ein-
träge miteinander zu verknüpfen [BB09] oder die Entwicklung einer Codebasis über der
Zeit zu visualisieren [Ha13]. Eine noch feiner granulierte Analyse der Entstehung eines
Programms erscheint für industrielle Anwendungen weniger sinnvoll, könnte im Bereich
der Programmierausbildung – analog zu der Beurteilung von Autoren – aber ein bisher
unausgeschöpftes Potential bergen.


2.2    Programmverständnis

Ebenfalls relevant ist die Disziplin des Program Understanding [WY96], bei der es u.a.
darum geht, die Anweisungen eines Programms den verschiedenen Teilzielen des Autors
zuzuordnen4 , um so ihre Bedeutung für die Gesamtlösung zu verstehen. Die Vorstellung
ist, dass der Entwickler einen Plan zur Umsetzung verfolgt, der aus verschiedenen Teil-
plänen besteht. Die Rekonstruktion des ursprünglichen Plans anhand des Quelltexts kann
3 Siehe bspw. http://www.writingpro.eu/logging_programs.php
4 z.B. besteht das Teilziel, einen Zähler zu implementieren, aus den Schritten Deklaration, Initialisierung und

 Inkrementierung, die jeweils an der richtigen Stelle (vor/innerhalb einer Schleife) stehen müssen.
auf Grundlage einer manuell gepflegten Hierarchie von Plänen/Aktionen inkl. zahlrei-
cher Nebenbedingungen gelingen [QYW98]. Diese Aktionen repräsentieren das Wissen
von Programmierern (Erfahrung, gute Programmierpraxis), das aber bei Programmier-
Anfängern meist erst noch entwickelt werden muss. Insofern ist der Erfolg dieser Tech-
niken bei Programmierlaboren fraglich. Andere Arbeiten verzichten auf ein tieferes
Verständnis und prognostizieren auf Basis von einfachen Kennzahlen der statischen Ana-
lyse (Anzahl Blöcke, Schleifen, ...) z.B. die Art des implementierten Sortier-Algorithmus
[TMK08].


2.3   Verständnisprobleme von Programmieranfängern

In der Literatur sind viele Fehlkonzepte von Programmieranfängern dokumentiert [BA01,
KP10]. Wir listen einige typische Probleme von Programmieranfängern auf, die vor allem
aber auch aus der eigenen Erfahrung im Bereich der Java-Programmierausbildung bekannt
sind:

1.     Richtung der Zuweisung: Wird die Anweisung a=b; als Zuweisung von links nach
       rechts, rechts nach links, oder als Vergleich aufgefasst?
2.     Ausführungsreihenfolge von Anweisungen der for-Schleife: Falsche Vorstellungen
       über den Zeitpunkt, zu dem die Bestandteile der Anweisung ausgeführt werden,
       provozieren ein Trial & Error Vorgehen.
3.     Statische Elemente: Wurde zuerst dem prozeduralen Paradigma gefolgt (Java: sta-
       tische Methoden und Attribute), fällt es den Studierenden beim Wechsel zum ob-
       jektorientierten Paradigma oft schwer, diesen statischen Elementen zu entsagen. Ei-
       ne ungebrochene Verwendung des Schlüsselwortes static ist ein Indiz, dass das
       nötige Verständnis noch nicht erworben wurde.
4.     Copy & Paste Bann: Meistens wissen die Studierenden, dass sie Copy & Paste nicht
       verwenden sollen, machen es aber dennoch.
5.     Test-Handling: Wird das Potential ggf. bereitgestellter Tests genutzt? Stellen die
       Studierenden sicher, dass sie bei der fortwährenden Erweiterung ihrer Lösung keine
       grünen Testfälle mehr verlieren?
6.     Weitsicht: Eine Aufgabe mittlerer Größe, für die JUnit-Tests vorliegen, ist typi-
       scherweise in Form von Interfaces vorspezifiziert. Die Studierenden kennen also
       ein Spektrum an Klassen und Methoden, die sie implementieren müssen. Wie gehen
       Studierende diese Aufgabe an? Erkennen sie, was aufeinander aufbaut, und fangen
       am Ende der Abhängigkeitskette an?     u.v.a.m.


3     Eine Beispielsituation
Zur Illustration einer typischen Labor-Situation betrachten wir die stark vereinfachte Mini-
Aufgabe aus Abb. 1, die einem Studenten zu einem Zeitpunkt gestellt wird, zu dem Ob-
jekte eingeführt wurden. Davor wurde Java nur prozedural behandelt (statische Attribute,
   statische Methoden). Daher bereitet es einigen Studierenden erfahrungsgemäß Schwierig-
   keiten, sich vom Schlüsselwort static loszusagen, wenn sie die Konzepte dahinter noch
   nicht verstanden haben. Ist das Konzept angekommen, ist die Aufgabe trivial und ein ent-
   sprechender Test (analog zum Inhalt von main()) bestätigt das richtige Ergebnis 5.

                                                    1 public class X {
   Aufgabe: Ein X soll eine ganze Zahl              2 public static void main(String [] args ) {
   speichern, die über get() abgefragt und         3    X x1 = new X();
   über set(int) neu gesetzt werden kann.          4    X x2 = new X();
   In der main()-Methode legen Sie zwei             5    x1. set (2);
   X-Objekte an, setzen ihren Wert auf 2            6    x2. set (3);
   bzw. 3, addieren den einen zum ande-             7    x1.add(x2);
   ren Wert und geben die Summe auf dem             8    System.out . println (x1. get ());
   Bildschirm aus.                                  9 }
                                                   10 }

                               Abb. 1: Ein sehr einfaches Beispielproblem.

 1 public class X {                           1 public class X {
 2   static int i ;                           2   static int i ;
 3   static int get () { return i ; }         3   static int get () { return i ; }
 4   static void set ( int ai ) { i=ai ; }    4   static void set ( int ai ) { i=ai ; }
 5   static void add(X x) { i+=x.i ; }        5   static void add(X x) { i+=X.i; }
 6                                            6
 7 public static void main(String [] args ) { 7 public static void main(String [] args ) {
 8     X x1 = new X();                        8     X x1 = new X();
 9     X x2 = new X();                        9     X x2 = new X();
10     x1. set (2);                          10     X.set (2);
11     x2. set (3);                          11     X.set (3);
12     x1.add(x2);                           12     X.add(x2);
13     System.out . println (x1. get ());    13     System.out . println (X.get ());
14 }                                         14 }
15 }                                         15 }

             Abb. 2: Ein Zwischenstand.                           Abb. 3: Eine Sackgasse.

   Beginnt der Student aber wie gewohnt mit statischen Attributen, zeigt Abb. 2 einen
   möglichen Zwischenstand, der aber natürlich das falsche Ergebnis (nämlich 6) liefert.
   Durch die Ausgabe oder den Unit-Test weiß der Student, dass etwas nicht stimmt, aber
   was unternimmt er? Ein (leider) typisches Verhalten von Studierenden ist es, sich hier (un-
   reflektiert) Ratschläge von der Entwicklungsumgebung zu holen. Eclipse macht mehrere
   Vorschläge. Einer ist change access to static für die Zeilen 10-13 aus Abb. 1. Folgt der
   Student konsistent diesem Vorschlag (er könnte potentiell auch inkonsistent verschiede-
   nen Vorschlägen folgen), führt ihn der Weg zu Abb. 3. Eine letzte verbleibende Warnung
   gibt es dort für Zeile 8: the value of local variable x1 is not used. Diese Meldung sollte ihm
   zu denken geben (Wie kann ich zwei Zahlen addieren, wenn eine davon gar nicht benutzt
   wird?), resultiert aber möglicherweise nur in einem Rückbau aller bisher vorgenommenen
   Änderungen. Der zweite Ratschlag, den Eclipse beim Stand von Abb. 2 macht, ist remove
static modifier of set(). Folgt er wieder konsistent demselben Vorschlag bei allen Warnung-
en, führt ihn dies zu der richtigen Lösung, ohne sich seinem eigenen Verständnisproblem
je gestellt zu haben.

Die erhaltene Version liefert das gewünschte Ergebnis, ein etwaiger Unit-Test ebenso. Vom
Endergebnis her ist es nicht von der Lösung eines Studenten, der die Problematik durch-
drungen hat, zu unterscheiden. Die wichtige Information über die Programmierfähigkeit
trägt der Weg zum Ziel, der üblicherweise nicht bekannt ist.


4     Lösungsvorschlag

4.1   Ansatz

Als Lösungsansatz für das skizzierte Problem wird hier eine Beobachtung der Studieren-
den während der Programmentwicklung vorgeschlagen. Die Erprobung im Rahmen einer
größeren Studierendengruppe steht noch bevor, bisher wurden nur im kleineren Rahmen
Entstehungsprotokolle erhoben. Die (geplanten) Elemente sind im Einzelnen:
• Automatische Protokollierung der Entstehungsgeschichte der Lösung durch ein Plug-
In in der Entwicklungsumgebung. Dies ermöglicht eine Playback-Funktion, so dass die
Entstehungsgeschichte im Nachhinein (zusammen mit dem Student oder nur durch den
Betreuer) betrachtet werden kann. Das Replay muss nicht in Echtzeit erfolgen und ist
damit zeitsparend, aber das eigentliche Ziel ist eine (semi-) automatische Analyse der
Aufzeichnungen.
• Eine klare Kommunikation an die Studierenden, dass die aufgezeichneten Informationen
in keiner Weise für die Bewertung herangezogen werden, dafür gibt es Klausuren und/oder
gesonderte Aufgaben/Projekte. Das Ziel ist es, Verständnisprobleme oder Schwächen di-
rekt und zielgerichtet im Labor oder der begleitenden Veranstaltung ansprechen zu können,
bevor es zu einer Klausur oder einem finalen Abschlussprojekt kommt. Dieses Ziel sollte
im Sinne der Studierenden sein, so dass sie sich freiwillig für eine Teilnahme entscheiden.
Die Protokollierung wird nicht zur Pflicht erhoben (ist abschaltbar).
• Im Idealfall liefern Indikatoren, die aus den Protokollen abgeleitet wurden, dem Betreuer
ein Indiz über problematische Abschnitte der Lösung, so dass eine gemeinsame Reflektion
gezielt angestoßen werden kann (und nicht erst nach Verständnisproblemen gesucht wer-
den muss). Häufig auftretende Probleme können dem Dozenten helfen, in der begleitenden
Vorlesung problematischen Stoff zu wiederholen und Hinweise auf weitere Aufgaben (in
der gleichen Richtung) zu liefern.


4.2   Verarbeitung

Die einfachste Form der Verarbeitung der Protokolle ist der Einsatz einer Playback-
Funktion. Sie ermöglicht die schrittweise Rekonstruktion, ist aber weiterhin zeitintensiv.
Für die automatische Auswertung wird nach jeder Änderung (mit Hilfe der Java Compiler
API) der aktuelle Quelltext und/oder der Syntaxbaum erzeugt, um Unterschiede in Quell-
textzeilen und/oder Knoten des Syntaxbaums zu identifizieren. Der Vorteil der lückenlosen
Protokollierung ist, dass sich die Quelltexte bzw. Syntaxbäume von Schritt zu Schritt nur
minimal unterscheiden und somit eine Beurteilung der (Art der) Änderung erleichtert wird
(vgl. ChangeDistiller [Fl07]). Quelltextzeilen und/oder Syntaxbaum-Knoten verschiede-
ner Evolutionsschritte werden einander zugeordnet und mit Änderungszählern ausgestat-
tet. Dabei liefert die Analyse auf Basis des Syntaxbaums klare Vorteile: (1) Die Änderung
kann präziser lokalisiert werden (z.B. Abbruchbedingung einer for-Schleife), (2) Forma-
tierungen oder Kommentare beeinflussen zwar die Position im Quelltext, aber nicht die
Lage des Baumknotens.
Außerdem können die einzelnen Änderungsereignisse zu abstrakteren Aktionen gebündelt
werden, etwa Bearbeitung der Methode f(), die in der Zeit verortet werden (Beginn / Ende
der Bearbeitung). Entsprechende Zeiträume lassen sich für die Erfüllung einzelner Tests
oder die Compilierfähigkeit von Klassen generieren.



4.3   Präsentation und Nutzung


Sind die Protokolle verarbeitet, können sie auf verschiedene Weise aufbereitet werden, um
Einblicke in den Kenntnisstand Einzelner oder der Gruppe zu geben:

1:public class X {                                 1:public class X {
1: private int value ;                             3: int i ;
1: public int get () {                             4: void set ( int ai ) {
1:   return value ;                                1:    i = ai ;
1: }                                               1: }
2: public void set ( int value ) {                 3: int get () {
1:    this . value = value ;                       1:   return i ;
1: }                                               1: }
1: public void add(X x) {                          3: void add(X x) {
1:    this . value += x. get ();                   3:    i += x. i ;
1: }                                               1: }
1: public static void main(String [] args ) {      1: public static void main(String [] args ) {
1:   X x1 = new X();                               1:   X x1 = new X();
1:   X x2 = new X();                               1:   X x2 = new X();
1:   x1. set (2);                                  3:   x1. set (2);
1:   x2. set (3);                                  3:   x2. set (3);
1:   x1.add(x2);                                   3:   x1.add(x2);
1:   System.out . println (x1. get ());            3:   System.out . println (x1. get ());
1: }                                               1: }
1:}                                                1:}

Abb. 4: Lösung eines Studenten mit den nötigen   Abb. 5: Lösung bei static-Schwierigkeiten.
Kenntnissen.                                       (gelb: mind. 2x, orange: mind. 3x modifiziert)
• Indikatoren – In ersten Tests hat sich der Eindruck ergeben, dass die Anzahl der compi-
lierbaren Zwischenschritte ein Indikator für den Reifegrad der Programmierfähigkeit sein
könnte. Wer noch unsicher ist, wird sich von compilierbarem Zwischenstand zu compi-
lierbarem Zwischenstand bewegen – sei es, weil sich nur kleine Schritte zugetraut werden,
oder weil den Fehlermarkierungen am Editor sehr viel Aufmerksamkeit gezollt wird. Je
größer die eigene Sicherheit und je weiter die Gedanken schon vorauseilen, desto eher
werden spontan weitere Aspekte berücksichtigt, noch bevor die letzte Anweisungen syn-
taktisch korrekt aufgeschrieben wurde. Viele weitere Indikatoren sind denkbar, wie etwa
die Lokalität von Änderungen (Fokus bei der Bearbeitung).

• HeatMap – Verständnisprobleme (wie die Probleme 1-3 des Abschnitts 2.3) führen da-
zu, dass die Studierenden für eine funktionierende Lösung (Unit-Tests erfüllt) bestimmte
Stellen wieder und wieder anfassen, bis es richtig läuft. Die Änderungshäufigkeit kann im
Code farbcodiert dargestellt werden, so dass Regionen hoher Änderungen sofort als Hot
Spot erkannt werden können. Damit sind die Stellen, die schwer gefallen sind, leicht zu
erkennen. Für die vom finalen Quelltext her nicht unterscheidbaren Beispiellösungen aus
Kap. 3 ergibt sich (bei der aktuell zeilenbasierten Änderungszählung) ein klarer Unter-
schied in Abb. 4-5. (Erfolgt die Änderungszählung und Farbcodierung nicht zeilenweise
sondern pro Syntaxbaum-Element wird noch deutlicher markiert, dass es eine große Un-
sicherheit bei den Modifikatoren gab.)

• Bearbeitungsmuster – Die Änderungshistorie kann verkürzt durch die Darstellung der
Zeitintervalle, in denen bestimmte Methoden bearbeitet oder Tests erfüllt wurden, visua-
lisiert werden (vgl. Abb. 6). Diese kompakte Darstellung erlaubt die Beurteilung von
Problemen 5 und 6 aus Abs. 2.3. Mit Methoden der Sequenzanalyse (z.B. [PHB12])
können die Entstehungshistorien aller Studierenden nach Gruppen unterschiedlicher Vor-
gehensweisen untersucht werden. Sind historische Protokolle verfügbar, wäre auch eine
Art Ratgeber-Funktion denkbar, die Anfängern Tipps gibt, um welche Dinge sie sich als
Nächstes kümmern könnten.
       Konstruktor(en)
           Methode f()
          Methode g()
                 Test 3
                 Test 2
                 Test 1
                                                                                        Zeit
Abb. 6: Zeitleiste mit Zeitintervallen der Bearbeitung (hell) und Testerfüllung (dunkel). Vertikale
Linien zeigen compilierbare Zustände an.



5    Fazit und Ausblick

Die Entstehungsgeschichte der Lösung zu einer Programmieraufgabe ist für den Dozen-
ten eine wertvolle Hilfe zur Beurteilung des Kenntnisstands des Autors. Die Erfassung
der Historie in Form eines (persistenten) Protokolls erlaubt verschiedene Auswertungen
(wie HeatMap, Bearbeitungssequenzen, ...), die rasch wertvolle Hinweise auf Defizite
des Autors liefern können, aber auch eine Beurteilung des Fortschritts der Gruppe ins-
gesamt ermöglichen. Bisher handelt es sich um vorläufige Untersuchungen im kleinen
Rahmen; ob und wie weit sich der Ansatz zu einem nützlichen Werkzeug im Alltag der
Programmierausbildung ausbauen lässt, soll im bevorstehenden Semester erprobt werden.
Möglicherweise sind dazu andere als die bisher üblichen Aufgabentypen angezeigt, bspw.
vorgegebene, fehlerhafte Quelltexte, die korrigiert werden sollen. Eine solche Korrektur ist
i.a. einfacher als die Konstruktion einer komplett eigenständigen Lösung und hatte viel-
leicht deswegen bisher wenig Wert, aber die Möglichkeit zur Beobachtung bei der Feh-
lersuche kann aufzeigen, wo im Quelltext ein Student eine mögliche Ursache gesehen hat
und damit wertvolle Einblicke in das Verständnis liefern.


Literaturverzeichnis
[BA01]    Ben-Ari, M: Constructivism in computer science education. Journal of Computers in
          Mathematics and Science, 20:45–73, 2001.
[BB09]    Bachmann, Adrian; Bernstein, Abraham: Data retrieval, processing and linking for soft-
          ware process data analysis. University of Zurich, Technical Report, (December), 2009.
[CCP07]   Canfora, Gerardo; Cerulo, Luigi; Penta, Massimiliano Di: Identifying Changed Source
          Code Lines from Version Repositories. International Workshop on Mining Software
          Repositories, S. 14, Mai 2007.
[Fl07]    Fluri, Beat; Würsch, Michael; Pinzger, Martin; Gall, Harald C.: Change Distilling : Tree
          Differencing for Fine- Grained Source Code Change Extraction. IEEE Trans. on Soft-
          ware Engineering, 33(11):725–743, 2007.
[Ha13]    Hanjalic, A: ClonEvol: Visualizing software evolution with code clones. Software Vi-
          sualization (VISSOFT), S. 1–4, 2013.
[KP10]    Kaczmarczyk, LC; Petrick, ER: Identifying student misconceptions of programming.
          Proceedings Computer Science Education, S. 107–111, 2010.
[Ma87]    Matsuhashi, Ann: Revising the plan and altering the text. In: Writing in real time: Mo-
          delling production processes. S. 197–223, 1987.
[PHB12] Peter, Sebastian; Höppner, Frank; Berthold, Michael R: Learning Pattern Graphs for
        Multivariate Temporal Pattern Retrieval. In: Proc. Int. Symp. Intelligent Data Analy-
        sis. Jgg. 7619 in LNCS. Springer, S. 264–275, 2012.
[PR96]    Piolat, Annie; Roussey, Jean-Yves: Students’ drafting strategies and text quality. Lear-
          ning and Instruction, 6(2):111–129, Juni 1996.
[QYW98] Quilici, Alex; Yang, Qiang; Woods, Steven: Applying Plan Recognition Algorithms To
        Program Understanding. Automated Software Engineering, 5:347–372, 1998.
[TMK08] Taherkhani, Ahmad; Malmi, Lauri; Korhonen, Ari: Algorithm recognition by static ana-
        lysis and its application in students’ submissions assessment. Int. Conf. on Computing
        Education Research, S. 88–91, 2008.
[WY96]    Woods, Steven; Yang, Q: The program understanding problem: analysis and a heuristic
          approach. In: 18th Int. Conf. Software Engineering. 1996.