Code lesen

Das tolle an Programmiersprachen ist, dass sie sich sehr ähneln.
Wenn eine verstanden wurde, können artverwandte zumindest auch gelesen werden!

processing sketch: many circles v1 (reds)
processing sketch: many circles v1 (reds)
von chromedecay (CC BY-NC-SA)

Eine Freundin hat beispielsweise den nachfolgenden Code-Snippet auf ihrem Blog veröffentlicht und sich gefragt, ob es auch lesbar ist für jemanden, der die Sprache nicht gelernt hat.

int frames = 235, num=30;
float theta;

void setup() {
  size(750, 750);
  rectMode(CENTER);
}

void draw() {
  background(20);
  noFill();
  translate(width/2, height/2);
  for (int i=0; i<num; i++) {
    pushMatrix();
    float offSet=TWO_PI/num*i;
    rotate(offSet);
    float sz = map(sin(theta), -1, 1, width*.15, width*.2);
    float x = map(sin(theta), -1, 1, sz, width*.2);
    translate(x, 0);
    pushMatrix();
    rotate(theta);
    stroke(200);
    if (i%2==0) {
      ellipse(0, 0, sz, sz*2);
      float px = cos(theta)*sz/2;
      float py = sin(theta)*sz;
      float sz2 = 20;
      strokeWeight(2);
      ellipse(px, py, sz2, sz2);
      strokeWeight(1);
    } else {
      fill(255,20);
      noStroke();
      ellipse(0, 0, sz*.7, sz*2*.7);
      float px = cos(theta+PI)*sz*.35;
      float py = sin(theta+PI)*sz*.7;
      float sz2 = 5;
      fill(255);
      ellipse(px, py, sz2, sz2);
      noFill();
    }
    popMatrix();
    popMatrix();
  }
  theta += TWO_PI/frames;
  //if (frameCount<=frames) saveFrame("image-###.gif");
}

Dabei fragte sie, was der Code wohl bewirken würde. Tipp beim nächsten Mal: Innerhalb von 

Quellcode

 wird Quellcode sogar hübsch aufbereitet 😉

Nehmen wir uns einmal obigen Code vor, bevor wir uns das Ergebnis ansehen.

int frames = 235, num=30;
float theta;

Hier werden zwei Ganzzahlen (int) und eine Gleitkommazahl einfacher Genauigkeit (float) deklariert, wobei ersteren auch gleich ein Wert zugewiesen wird (sprich: Sie werden initalisiert). Einfache Genauigkeit sagt etwas darüber aus, wieviel Stellen hinter dem Komma ich brauchbare Ergebnisse erwarten darf (grob acht in diesem Falle). Die Datentypen int und float erlauben es dem Compiler (das ist das Werkzeug, welches obige Anweisungen in Maschinencode (0 und 1) übersetzt) einige Optimierungen hinsichtlich des Speicherbedarfs vorzunehmen.

void setup() {
  size(750, 750);
  rectMode(CENTER);
}

Hier wird eine Funktion namens setup deklariert. Sie benötigt keine Argumente (siehe ()) und gibt nichts zurück (void). Innerhalb von setup werden die Funktionen size und rectMode ausgeführt. CENTER dürfte laut Konvention eine Konstante sein (alles in Großbuchstaben). Die Funktion an sich wird hier noch nicht ausgeführt. Ich nehme an, dass size ein Fenster der Größe 750×750 anlegt und rectMode dafür sorgt, dass nachfolgende Befehle den Mittelpunk (375, 375) als Referenz benutzen.

void draw() {
  background(20);
  noFill();
  translate(width/2, height/2);
  // ...
}

Eine weitere Funktion namens draw. In ihr werden mehrere Anweisungen ausgeführt und kein Wert zurückgegeben. Zunächst wird die Funktion background mit dem Argument 20 aufgerufen, danach noFill und translate. Ich müsste in der Dokumentation nachschlagen, was background tut. noFill weist etwas an, es nicht auszufüllen. Vermutlich dem Hintergrund. translate sorgt für eine Verschiebung um width/2 und height/2, also in das Zentrum des Fensters.

  for (int i=0; i<num; i++) {
    pushMatrix();
    float offSet=TWO_PI/num*i;
    rotate(offSet);
    float sz = map(sin(theta), -1, 1, width*.15, width*.2);
    float x = map(sin(theta), -1, 1, sz, width*.2);
    translate(x, 0);
    pushMatrix();
    rotate(theta);
    stroke(200);
    // ...
  }

Wechseln wir in eine for-Schleife. Sie benutzt die Zählvariable i (Ganzzahl) von 0 bis zum oben definierten num (30) und wird in jedem Durchlauf um 1 erhöht (i++ bedeutet i += 1, bedeutet i = i+1, bedeutet „erhöhe i um 1 und weise das Ergebnis i zu„).

In jedem Schleifendurchlauf wird pushMatrix ausgeführt, um etwas auf den Stapel Matrix hinzuzufügen (zu pushen). Stapel kann sich wie ein Kartenstapel vorgestellt werden, auf dem entweder eine Karte draufgelegt wird (push) oder die oberste entfernt (pop).

Danach wird in jedem Durchlauf eine neue Variable namens offSet angelegt und der Gleitkommawert TWO_PI/num*i zugewiesen. TWO_PI ist wieder eine Konstante (rund 2π, je nachdem, wie viele Nachkommastellen in ein float passen). offSet bezeichnet gemeinhin eine Verschiebung innerhalb einer Liste. Hier wird es benutzt, um um den Winkel offSet zu drehen (rotate).

Jetzt werden zwei weitere Variablen sz und x deklariert und initalisiert. map bezeichnet eine Funktion, die mit jedem Eintrag in einer Liste etwas tut. sin schlägt innerhalb einer Tabelle den Wert (in Grad oder Bogenmaß) für theta (also Bogenmaß) nach, um Rechenzeit zu sparen. .Zahl ist eine Kurzschreibweise für 0.Zahl, welche vor allem in den USA üblich ist.

stroke schließlich zeichnet etwas. Im Allgemeinen eine Gerade.

    if (i%2==0) {
      ellipse(0, 0, sz, sz*2);
      float px = cos(theta)*sz/2;
      float py = sin(theta)*sz;
      float sz2 = 20;
      strokeWeight(2);
      ellipse(px, py, sz2, sz2);
      strokeWeight(1);
    } else {
      // ...
    }

Abhängig vom Ergebnis der Bedingung i%2 == 0 wird der Code im ersten Block (alles zwischen { und }) oder im zweiten (alles hinter else { und }) ausgeführt. Die Bedingung prüft hierbei, ob die Schleifenvariable i gerade (i modulo 2 ist 0) oder ungerade (i modulo 2 ist nicht 0, d.h. der Rest der Division von i durch 2 ist 1) ist. Im Falle der Geradheit wird die Funktion ellipse ausgeführt, die vermutlich eine Ellipse um (0,0) mit den Radien sz und sz*2 zeichnet. Anschließend werden wieder Gleitkommazahlen px und py berechnet, sowie sz2 auf den Wert 20 gesetzt. strokeWeight ist wahrscheinlich für die Dicke (weight) des Striches (stroke) zuständig und wird nach einer Weiteren Ellipse um (px, py) mit den Radien sz2 und sz2 (= einem Kreis mit diesem Radius) wieder zurückgesetzt.

    if (i%2==0) {
      // ...
    } else {
      fill(255,20);
      noStroke();
      ellipse(0, 0, sz*.7, sz*2*.7);
      float px = cos(theta+PI)*sz*.35;
      float py = sin(theta+PI)*sz*.7;
      float sz2 = 5;
      fill(255);
      ellipse(px, py, sz2, sz2);
      noFill();
    }

Betrachten wir den Fall, dass i ungerade ist. Dann wird etwas mit 255 und 20 gefüllt. Wieder müsste ich nachschlagen, was fill tut. Jedenfalls wird nichts gezeichnet (noStroke) und anschließend eine Ellipse gemalt. Im Wesentlichen läuft es nicht viel anders ab als oben, nur dass die Argumente andere sind.

  for (int i=0; i<num; i++) {
    // ...
    popMatrix();
    popMatrix();
  }
  theta += TWO_PI/frames;
  //if (frameCount<=frames) saveFrame("image-###.gif");
}

Hier werden zwei Mal Werte aus Matrix abgerufen (die oben mit push darauf abgelegt wurden). Danach ist die Schleife abgelaufen und theta wird auf einen neuen Wert gesetzt.

Die auskommentierte Anweisung (hinter //) würde dafür sorgen, dass das betreffende Bild als image-###.gif abgespeichert werden würde, wobei ### durch den Wert frameCount ersetzt worden wäre.

Wenn ich mit also das Ergebnis ansehe, sieht es hübsch aus. Die Programmiersprache nennt sich Processing (ich vermiss mein c’t-Heft mit dem Spezialthema Processing immer noch :-/). Die Syntax ähnelt C++, weswegen ich in obigen Code-Ausschnitten die Syntax-Hervorhebung auf C++ stellte.

Abschließend muss ich sagen, dass der Code noch Optimierungsbedarf hat. So könnten die lokalen Variablen (alles innerhalb der Funktion) einmal vor der Schleife deklariert werden und nur innerhalb der Schleife ihren Wert zugewiesen bekommen, statt in jedem Durchgang erneut erstellt und verworfen zu werden. Daneben ist die Schreibweise nicht einheitlich (siehe Leerzeichen um =). Die Variablen sind auch nicht immer „sprechend“, sondern müssen erst einmal durchdacht werden, bevor sich ihr Sinn erschließt.

Advertisements

Ein Gedanke zu “Code lesen

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s