Einführung

Warum soll man programmieren lernen?
  • Vielleicht, um zu verstehen wie ein Nachrichtendienst an verschlüsselte Daten kommt?
  • Vielleicht, um zu sehen, wie das Innere von ''Alexa'' ausschaut?
  • Vielleicht, um ein Programm zu schreiben, das mir die Hausaufgaben von allein ausrechnet?
  • Vielleicht, um mal selber einen Virus, ein Chatprogramm, eine bewegte Minischlange zu programmieren?
  • Wir benutzen tagtäglich Computer, sie haben unseren Lebensstil seit einigen Jahren komplett verändert. Ist es da nicht lohnenswert, ein wenig zu verstehen wie sie im Innern arbeiten?
  • Die Verarbeitung der Daten in einem Computer gibt uns Aufschluss über unser eigenes Denken: Wo es anders ist oder wo es erstaunlich ähnlich verläuft. Als Maschine kann ein Computer zwar nicht eigenständig denken (?), aber er kann einige Schritte, die beim Denken eine große Rolle spielen, äußerst effizient ausführen.
Der Computer kann
  • Daten einlesen, speichern und ausgeben
  • sehr schnell rechnen
  • einfache Wenn-dann-Entscheidungen treffen
  • ohne Langeweile etwas zigmal wiederholen
  • Zufälle erzeugen
Diese fünf Funktionen genügen, um den Hauptteil der Arbeit eines Computers nachvollziehen zu können. Das soll anhand einiger schöner Beispiele geschehen, die es euch auch ermöglichen, die Mathe-Hausaufgaben schneller zu erledigen.

Als Programmiersprache verwenden wir "Perl". Falls ihr zu Hause weiterprogrammieren wollt, könnt ihr den Perl-Interpreter bei Active Perl herunterladen. Ausführlichere Einweisungen gibt es im Internet sehr viele, z.B.:
www.mathe2.uni-bayreuth.de/perl/inhalt.htm
www.ssw.uni-linz.ac.at/Teaching/Lectures/Sem/2000/Katzmayr/
http://de.wikibooks.org/wiki/Perl-Programmierung
www.tutorialspoint.com/perl/perl_syntax.htm

Einstieg: Kryptische Botschaften

Früher wurden Texte buchstabenweise per Hand verschlüsselt. Bei langen Texten und heutigen komplizierten (d.h. sichereren) Verschlüsselungsverfahren hat sich der Computer als unentbehrlich erwiesen. Stellt euch vor ihr bekommt folgende Nachricht:

87 97 115 32 115 111 108 108 32 100 101 110 110 32 100 97 
115 32 98 101 100 101 117 116 101 110 63

Das ist jetzt ein offizieller, d.h. nicht geheimer Code: Entziffert werden kann die Nachricht, indem ihr eine ASCII (oder ANSI-) Tabelle zur Hilfe nehmt und jede Zahl wieder in ihren zugehörigen Buchstaben umformt.
ASCII - American Standard Code for Information Interchange
ANSI - American National Standards Institute
Einen link findet Ihr hier: ASCII-Tabelle.
Ihr seht: Bei längeren Texten wird das mühselig. Und wenn die Nachricht erst

zdvvrooghqqgdvehghxwhq

lautet, ohne dass wir den Code kennen, wie können wir den Computer zur Hilfe nehmen? Dazu müssen wir zunächst Text-Ein- und Ausgabe erlernen und dann Verarbeitung von Text in Schleifen (Wiederholungen).

Mein erstes Perl-Programm

Als erstes Perl-Programm schreiben wir einen einfachen Text:

hello.pl
======================================================================
print "Hello World!\n";
======================================================================

Dieses Programm (also das, was zwischen den ==-Zeichen steht) kopiert ihr hier aus der Webseite in euren Editor und speichert es dann in eurem Verzeichnis ab unter dem Namen "hello.pl". Dann macht ihr in diesem Verzeichnis eine shell auf, ein Terminalfenster (schwarz mit weißer Schrift), dort gebt ihr den Befehl "perl hello.pl" ein und drückt <Enter>. Was passiert?

Ihr könnt das Programm auch etwas eleganter schreiben, indem Ihr eine Variable verwendet. Der Output ist derselbe, aber für spätere Anwendungen kann man nun die Variable "satz" in dem Programm bearbeiten. Beachte: 
Vor einer Variablen muss immer das Dollarzeichen "$" stehen. 
Befehle/Funktionen werden in Perl immer mit einem Semikolon ";" abgeschlossen.
Was ändert sich, wenn man das Zeichen "\n" weglässt?

hello.pl
======================================================================
$satz = "Hello World!";
print "$satz\n";
======================================================================

Nun möchte man, dass das Programm auch etwas tut. Es wäre doch schön, wenn der Computer uns an unserem Arbeitsplatz freundlich begrüßen würde. Dafür lernen wir den Befehl <STDIN> d.h. Standardinput, hier kann der Programmbenutzer eine Eingabe machen. Der nachfolgende Befehl "chomp" ist rein technisch: Er löscht alle Sonderzeichen, wie z.B. den Eingabeklick.
Da es Ausgabe und Eingabe gibt, werden im folgenden Programm zwei Variablen, nämlich $satz und $antwort verwendet, ihr könnt sie aber beliebig umbenennen.
Schau genau hin, wie durch die Punkte in der vorletzten Zeile die $antwort in den Satz eingebunden wird.

hello.pl
======================================================================
$satz = "Hallo, wie geht es dir?";
print "$satz\n";
$antwort = <STDIN>; 
chomp($antwort);
$satz = "Es freut mich, dass es dir ".$antwort." geht!";
print "$satz\n";
======================================================================

Aufgabe: Schreibe ein Programm, das dich nach deinem Namen fragt und dann damit begrüßt.

If-Abfragen: Smalltalk mit dem PC

talk.pl
======================================================================
print "Was ist dein Lieblingsgericht?\n";
$antwort = <STDIN>; 
chomp($antwort);
if($antwort eq "Sellerieschnitzel")
   {
   print "hmmm, lecker ".$antwort." liebe ich auch!!!\n";
   }

if($antwort eq "Spaghetti")
   {
   print "igittigitt, ".$antwort."\n";
   }
======================================================================

Hier wurde eine einfache Wenn-Dann-Unterscheidung eingefügt. Die Syntax hierfür lautet 
if (bedingung) 
   { 
   führe das hier aus; 
   }

Hiermit könnten wir jetzt eine ganze Unterhaltung zusammenbauen. Der Computer ist der perfekte Gesprächspartner: Er weiss alles und er läuft nicht weg. Ihr müsst nur im voraus ein wenig erahnen, was wohl der Computerbenutzer fragen und antworten will. Programmiere ein ganzes Gespräch über Essen und Wetter und Lehrer etc. und wechsel dann mit Deinem Nachbarn den Computer, so dass ihr gegenseitig eure Gespräche testen könnt. 

Wenn ihr alles super perfekt gemacht habt, sodass euer Nachbar nicht erkennt, ob nur ein Programm antwortet oder ein verborgener, per Internet verbundener Chat-Partner, dann habt ihr eine perfekte Turing Maschine gebaut, eine die den Turing-Test besteht (nach Alan Turing, einem britischen Geheimdienst-Mathematiker, der deutsche Codes knackte und sich dabei überlegte, was ist grundlegend der Unterschied zwischen menschlicher und maschinlicher "Denkweise").

Kleine Rechenübungen

rechner.pl
======================================================================
$zahl1 = 5;
$zahl2 = 7;
$zahl3 = $zahl1*$zahl2;
print "Ergebnis: $zahl3\n";
======================================================================

Nun ist es mühsam, wenn wir für jede Rechnung das Programm umschreiben müssen, daher erweitern wir das obige Programm, so dass wir beim Laufen des Programmes die Zahlen selber eingeben können.

rechner.pl
======================================================================
print "Erste Zahl:";
$zahl1 = <STDIN>; 
chomp($zahl1);
print "Zweite Zahl:";
$zahl2 = <STDIN>; 
chomp($zahl2);
$zahl3 = $zahl1*$zahl2;
print "Ergebnis von $zahl1 * $zahl2: $zahl3\n";
======================================================================

Der große Vorteil vom Computer im Gegensatz zum Taschenrechner ist, dass die Anzeige (die Mantisse) mit weitaus größerer Genauigkeit erfolgen kann.

So kann man sonderbare Gesetzmäßigkeiten von ganzen Zahlen finden:
11 * 11 =
111 * 111=
1111 * 1111 = 
...
111111111 * 111111111 = 
oder
9 * 7 =
99 * 77 =
999 * 777 =
...
99999 * 77777 =
...
oder schau genau hin was passiert wenn man
1159420289855072463768 * 7 nimmt...
oder
10987654321 * 9 = 
...
oder warum ergeben folgende Produkte genau diese Ergebnisse:
12006 * 12006 =
14007 * 14007 =
...
300015 * 300015 =
Manche solcher Rechnungen sind reine Spielerei, manche führen aber auch in kurzer Zeit tief in das Gebiet der Zahlentheorie und damit in offene Forschungsbereiche...

Nun wollen wir aber nicht nur multiplizieren, sondern auch addieren, subtrahieren, dividieren ... daher schreiben wir unser Programm erneut um.

Programm: Taschenrechner

rechner.pl
======================================================================
print "---- Start Programm Taschenrechner ----\n";
print "Erste Zahl:";
$zahl1 = <STDIN>; 
chomp($zahl1);
print "Operation:";
$zeichen = <STDIN>;
chomp($zeichen);
print "Zweite Zahl:";
$zahl2 = <STDIN>; 
chomp($zahl2);

$ergebnis = 0;
$matherror = 1;

if($zeichen eq "+")
   { 
   $ergebnis = $zahl1+$zahl2;
   $matherror = 0;
   }
if ($zeichen eq "-") 
   {
   }

if($matherror == 0)
   {
   print "Ergebnis: $zahl1 $zeichen $zahl2 = $ergebnis\n";
   }
else
   {
   print "MathError\n";
   }
======================================================================

 

Aufgabe: Beobachte genau die Variable $matherror. Wozu soll sie dienen, wie wird mit ihr umgegangen? 

Aufgabe: Vervollständige das Programm so, dass es alle vier Grundrechenarten ausführen kann. Zur Arbeitserleichterung ist es beim Programmieren vorteilhaft viel mit Copy and Paste zu arbeiten.

Aufgabe: Vervollständige das Programm so, dass beim Teilen ein Schutz MathError gegen die Division durch Null eingebaut ist. 
(Hilfe: if ($zahl2==0) {} fragt, ob $zahl2 Null ist,
oder if ($zahl2!=0) {} fragt, ob $zahl2 ungleich Null ist, an der richtigen Stelle einfügen.
Die Syntax für geschachtelte if-Abfragen mit Alternativen lautet 
if (bedingung eins) 
   { 
   falls bedingung eins zutrifft, führe das hier aus; 
   if (bedingung zwei) 
      { 
      falls bedingung eins und zwei zutrifft, führe jetzt das hier aus; 
      }
   else
      { 
      falls bedingung eins zutrifft, aber nicht bedingung zwei, führe das hier aus; 
      }
   }

Gib genau acht, wie die einzelnen Klammern stehen!

Aufgabe: Perl kennt auch die Funktion sqrt($zahl): square root = Quadratwurzel. Vervollständige das Programm so, dass es auch Quadratqurzeln ziehen kann. Die neue Operation könnte "sqrt" heissen, der Name kann aber beliebig sein. Da die zweite Zahl dann unnötig wird, soll sie durch eine weitere if-Abfrage gestrichen werden.

Aufgabe: Vervollständige das letzte Programm so, dass bei der Funktion sqrt($zahl) ein Schutz MathError eingebaut ist, falls $zahl kleiner als Null ist.

Aufgabe: Schreibe ein neues Programm abc.pl, das die abc-Formel zur Lösung quadratischer Gleichungen berechnet. Es soll zunächst nach a, b und c fragen und deren Werte einlesen. Als nächstes soll es die Diskriminante D berechnen (Erinnerung: D=b*b-4*a*c) und soll ausgeben, ob es zwei, eine oder keine Lösung der zugehörigen quadratischen Gleichung gibt. Die Lösung soll am Schluss ebenfalls ausgegeben werden.
abc-Formel

Schleifen - Wiederholungen - Zählen: Rechnen am Fließband

Ein Computer kann perfekt wiederholen:
Ein Computer kann perfekt wiederholen:
Ein Computer kann perfekt wiederholen:
Ein Computer kann perfekt wied...

loop.pl
======================================================================
$satz = "Soll ich fortfahren? ";
$antwort = "ja"; 
while( $antwort eq "ja" )
  {
  print "$satz";
  $antwort = <STDIN>; 
  chomp($antwort);
  }
======================================================================

Eine andere Art zu wiederholen besteht in dem zählenden for-loop. Das Beispiel unten zählt bis 13, bei jedem Durchgang wird der Zähler i um 1 erhöht:

loop.pl
======================================================================
for($i=0;$i<14;$i=$i+1)
  {
  print "$i Semmeln weg\n";
  }
======================================================================

Aufgabe: Verändere das Programm so, dass der kleine Sprachfehler eliminiert wird.

Aufgabe: Verändere das Programm so, dass zunächst nach der oberen Grenze der Semmeln gefragt wird, die dann per Eingabe gesetzt werden kann.

Aufgabe: Verändere das Programm so, dass es die Zweierreihe ausgibt, oder die Fünferreihe.
Hier gibt es zwei plausible Lösungen, versuche unbedingt beide zu finden!!

Aufgabe: Verändere das Programm so, dass es die ersten 10 ungeraden Zahlen ausgibt.

Aufgabe: Verändere das Programm so, dass es die ersten 10 Quadratzahlen ausgibt.

Aufgabe: Verändere das Programm so, dass es das gesamte kleine Einmaleins ausgibt. In der ersten Zeile die Einerreihe, in der zweiten die Zweierreihe etc. (Hilfe: Man kann zwei ineinandergeschachtelte for-loops verwenden, genauso wie bei den ineinandergeschachtelten if-Abfragen. Ein loop zählt mit einem i, der zweite mit einem j.)


Man kann die beiden obigen Programme kombinieren, um zu verstehen, was verschachtelte Loops sind (oder einfach nur um den Computerbesitzer zu nerven). 
Aufgabe: Versuche zu verstehen, was das folgende Programm macht, ohne es laufen zu lassen. Danach setze es mit Perl in Bewegung und schau, ob deine Vorhersage stimmte!

loop.pl
======================================================================
$satz1 = "Soll ich ";
$satz2 = "fortfahren? ";
$satz = $satz1.$satz2;
$repeat = 0;
$antwort = "ja"; 
while( $antwort eq "ja" )
  {
  $satz = $satz1;
  for($i=0;$i<$repeat;$i=$i+1)
    {
     $satz = $satz."wirklich ";
    }
  $satz = $satz.$satz2;
  print "$satz";
  $antwort = <STDIN>; 
  chomp($antwort);
  $repeat = $repeat + 1;
  }
print "Na, dann eben nicht!\n";
======================================================================

Aufgabe: Jetzt zurueck zur Mathematik: Versuche zu verstehen, was das folgende Programm macht, ohne es laufen zu lassen. Danach setze es mit Perl in Bewegung und schau, ob deine Vorhersage stimmte! Das Programm zeigt, wie ein Computer im Innern rechnet.

power.pl
======================================================================
$produkt = 0;
$faktor = 4;
$grenze = 7;
for($i=0;$i<$grenze;$i=$i+1)
  {
  $produkt = $produkt+$faktor;
  }
print "$produkt\n"
======================================================================

Aufgabe: Verändere das Programm so, dass es Potenzen berechnen kann.

Aufgabe: Verändere das Programm so, dass es Fakultäten berechnen kann.
(Erinnerung: 5! = 1*2*3*4*5)

Aufgabe: Füge die beiden Funktionen Potenz und Fakultät in den Taschenrechner rechner.pl ein. Jetzt haben wir schon ein richtig leistungsfähiges Programm selbst geschrieben!

Aufgabe: Versuche zu verstehen, was das folgende Programm macht, ohne es laufen zu lassen. Danach setze es mit Perl in Bewegung und schau, ob deine Vorhersage stimmte!

wasistdas.pl
======================================================================
$k = 0;
$grenze = 7;
for($i=0;$i<$grenze;$i=$i+1)
  {
  $k = $k+2*$i+1;
  print "$k\n";
  }
print "Richtig gedacht?\n";
======================================================================

Bewegung im Computer: Virtual Worlds for Rookies

Bewegung auf dem Bildschirm ist immer faszinierend. Die digitale Animation ist so etwas wie ein Homunculus der Neuzeit: Ein künstlich geschaffener eigener Organismus.
Hier ein paar Einstiegsbeispiele:

bewegung1.pl
======================================================================
$|++;            # wichtig, zum Initialisieren
print "Warte! Ich arbeite gerade!\n";
$grenze = 5;
for($i=0;$i<$grenze;$i=$i+1)
   {
   print "|";
   sleep(1);      # 1 Sek Pause
   }
print "\n";
print "Fertig!\n";
======================================================================

Das ist der erste Schritt zum grossen Kino. Dasselbe Programm nochmal mit einer Subroutine, so dass, die eigentliche Zeichnungs-Funktion kompakt ausgelagert werden kann (brauchen wir später, wenn größere Bilder kommen).

bewegung2.pl
======================================================================
sub printout
{
   print '|';
}


$|++;            # wichtig, zum Initialisieren
print "Warte! Ich arbeite gerade!\n";
$grenze = 5;
for($i=0;$i<$grenze;$i=$i+1)
   {
   printout;
   sleep(1);      # 1 Sek Pause
   }
print "\n";
print "Fertig!\n";
======================================================================

Da der einleuchtende Befehl sleep nur für Pausen mit ganzzahligen Sekunden taugt, also etwas unflexibel ist, werden wir ihn im Folgenden durch den Befehl select undef, undef, undef, 0.5; ersetzen, der auch halbe oder zehntel Sekunden akzeptiert, da sonst die Bewegung für die meisten Anlässe zu langsam ist. 
Und noch etwas geht anders: Bewegung auf der Stelle! Im folgenden Programm ist der Print-Befehl mit einem Zusatz "b" versehen: backwards.

bewegung3.pl
======================================================================
$|++;            # wichtig, zum Initialisieren
$grenze = 5;
for($i=0;$i<$grenze;$i=$i+1)
   {
   print "$i", "\b";
   select undef, undef, undef, 0.5;
   }
print "\n";
print "Fertig!\n";
======================================================================

Mit der etwas kryptisch anmutenden Funktion substr, die einen String, also ein Wort oder einen Satz zerteilen, einzeln auslesen kann, kann man mit den bisherigen leichten Hilfsmitteln das bekannte Warte-Rädchen basteln, das immer noch in vielen Anwendungsprogrammen auftaucht:

bewegung4.pl
======================================================================
$|++;            # wichtig, zum Initialisieren
$wheel = 12;
for($j=0;$j<$wheel;$j=$j+1)
   { 
   print substr( "-/|\\", $j % 4, 1 ), "\b"; 
   select undef, undef, undef, 0.1; 
   }
print "\n";
print "Fertig!\n";
======================================================================

Aufgabe: Kombiniere die Programme bewegung2.pl und bewegung4.pl so, dass langsam eine Strichreihe entsteht, wie in 1, zwischen den einzelnen Strichen jedoch nicht nur gewartet wird, sondern das Warte-Rad läuft, wie in 3. Dazu benötigt ihr entweder zwei ineinandergeschachtelte for-loops, genauso wie bei den ineinandergeschachtelten if-Abfragen (Eine Schleife zählt mit einem i, der zweite mit einem j), oder ihr deklariert das Wheel als kleine Subroutine, wie in bewegung2.pl und ruft diese Routine mehrmals auf. 

 

Jetzt wird's ernst: Unten ist die Figur einer wartenden Schlange programmiert. Probiert sie zunächst mal als Programm aus.

Aufgabe 1: Die Schlange soll sich mit jedem Biss nach vorne bewegen. 

Aufgabe 2: Vor der Schlange soll sich irgendeine Schrift befinden, schrittweise soll die Schlange sich die Schrift einverleiben. Ob die Schlange dabei länger wird (oder gar dicker) oder unverändert bleibt während der Bewegung, ist euch überlassen. 

bewegung5.pl
======================================================================
$|++;                     # wichtig, zum Initialisieren
print "xxx(",chr(248);    # hierbei ist chr(248) das Grad-Symbol ° , man kann auch einfach "o" nehmen
for (0..10) 
   { 
   print "<";
   select undef, undef, undef, 0.1; 
   print "\b";
   print "-";
   select undef, undef, undef, 0.1; 
   }
print "\n";
======================================================================

Die Syntax des for-loops hat sich auch ein wenig geändert, das geht so, wenn man den index als Zahl nicht braucht. Perl hat oft verschiedene Schreibweisen.

Letzter Punkt: Der ganze Bildschirm soll erobert werden. Das ist nun leider sehr Betriebssystem abhängig. Bei mir funktionieren unter Linux-Mint folgende Befehle:

bewegung6.pl
======================================================================
$|++;                     # wichtig, zum Initialisieren
print "\033[2J";          #clear the screen 
print "\033[0;0H";        #jump to 0,0 
print "x";
select undef, undef, undef, 0.5; 
print "\033[20;0H";       #jump to 20,0 
print "x";
select undef, undef, undef, 0.5; 
print "\b";
select undef, undef, undef, 0.5; 
print "\033[10;45H";      #jump to 10,45 
print "x";
======================================================================

Aufgabe: Entwerfe ein einfaches Strichmännchen, das gehen oder winken oder sich setzen kann. 

Aufgabe: Lass die Schlange aus bewegung5.pl über der gesamten Bildschirm wandern, nicht nur eine Zeile.

Arrays: Ordnung ist das halbe Leben

Hat man viele Daten auf einmal, lohnt es sich oft, sie aneinander zu ketten und durchzunummerieren, so dass man geordnet auf sie zurückgreifen kann. Das geschieht mit sogenannten Arrays (dt. Felder)
Das Sonderzeichen, das in Perl Arrays kennzeichnet ist @.

arrays.pl
======================================================================
@blubb;
$blubb[0]='Bilbo';
$blubb[1]='Frodo';
$blubb[2]='Sam';
$blubb[3]='Peregrin Tuk';

print "$blubb[1] ist ein Hobbit \n"; 

print "Es gibt viele Hobbits, z.B.: \n"; 
for($i=0; $i<=3; $i=$i+1)
  {
  print "$blubb[$i] \n";
  }
======================================================================

Die Variable blubb ist ein Array, d.h. man kann viele Zahlenwerte oder Wörter darin speichern, nicht nur einen Wert. Belegt und ausgelesen werden die Werte eines Arrays über einen Index 0, 1, 2, ...

 


Was kann man nun mit Arrays machen? 

Zum Beispiel können wir mit dem Wissen von Arrays und Loops nun unser Smalltalk-Programm kompakter und damit wandlungsfähiger schreiben:

talk.pl
======================================================================
@gericht=('Spaghetti','Pommes Frites','Sellerieschnitzel','Brei');
@geschmack=('igittigitt','ja, das mag ich auch','hmmm, lecker','ja gehts noch');
print "Was ist dein Lieblingsgericht?\n";
$antwort = <STDIN>; 
chomp($antwort);
if($antwort eq $gericht[1])
   {
   print "$geschmack[1], ".$antwort."!!!\n";
   }
======================================================================

Aufgabe: Verändere das Programm so, dass nicht nur gericht[1], also "Spaghetti", abgefragt wird, sondern alle aufgelisteten Gerichte.

 

Arrays sind im Zusammenhang mit den Rechenfähigkeiten ein äußerst starkes Werkzeug. Für Anfänger werden die Programme allerdings manchmal wegen der Indizes ein wenig schwer lesbar. Versuche nachzuvollziehen, was das folgende Programm macht, ohne es laufen zu lassen. Danach setze es mit Perl in Bewegung und schau, ob deine Vorhersage stimmte!

sort.pl
======================================================================
# Zeilen, die mit diesem Zeichen beginnen, sind Kommentarzeilen.
# Sie dienen dazu, das Programm lesbarer zu gestalten
@blubb=(12,15,-33,0.4,2,-199,20000,-3017,529,1,-34,9090,12,13,-15,187321,-10862,0.002,100,-100);
# blubb ist ein array von gemischten Zahlen
$laenge = @blubb;
# laenge ist jetzt die Anzahl der Einträge in dem array blubb (derzeit 19 Stück)
for ($i=0;$i<$laenge;$i=$i+1)
  {
  print "$blubb[$i] \n";
  }
# Erstmal alles ausgeben, was gespeichert wurde
$ende="nein";
while($ende eq "nein")
  {
  $ende="ja";
  for($i=0;$i<$laenge-1;$i=$i+1)
    {
    if($blubb[$i]>$blubb[$i+1])
      {
      ($blubb[$i],$blubb[$i+1])=($blubb[$i+1],$blubb[$i]);
      $ende="nein";
# Genau hinschauen was passiert: Die Einträge im array blubb werden vertauscht!
      }
    }
  }
print "\n";
print "Jetzt nochmal:\n";
print "\n";
for ($i=0;$i<$laenge;$i=$i+1)
  {
  print "$blubb[$i] \n";
  }
======================================================================

Das folgende Programm berechnet noch einmal die Quadratzahlen, speichert sie diesmal aber in einem array ab.

Aufgabe: Lass das Programm zunächst einfach laufen und sei sicher, dass Du alle Schritte verstehst. Verändere dann das Programm so, dass es die ersten 10 Fibonacci-Zahlen in einem array speichert und ausgibt.
Erinnerung: 1  1  2  3  5  8  13  21 ...

zahlen.pl
======================================================================
$grenze = 10;
@zahlen;
$zahlen[0]=1;
for($i=1;$i <= $grenze;$i=$i+1)
  {
  $zahlen[$i] = $zahlen[$i-1]+2*$i+1;
  }
for($i=0;$i <= $grenze;$i=$i+1)
  {
  print "$zahlen[$i] "
  }
print "\n"
======================================================================

Aufgabe: Verändere das Programm so, dass zunächst nach der oberen Grenze n gefragt wird und dann die ersten n Fibonacci-Zahlen ausgegeben werden. Füge diese Funktion dann in unser Taschenrechner-Programm ein. Jetzt haben wir schon einen Taschenrechner, der deutlich mehr kann als alle normal gebräuchlichen auf dem Markt!

Jetzt sind wir bereit für wirklich anspruchsvolle Aufgaben:

Primzahlen

Das folgende Programm testet, ob eine gegebene Zahl durch vier teilbar ist. Der entscheidende Schritt hierbei ist die Funktion:$zahl%$k, die angibt, was für ein Rest bleibt, wenn man $zahl durch $k teilt (also bei $zahl=7 und $k=4 würde $zahl%$k 3 ergeben). Falls dieser Rest 0 ist, so ist die Zahl durch $k teilbar.

prim.pl
======================================================================
$k=4;
print "Zahl:";
$zahl=<STDIN>;
chomp($zahl);
$teilbar="nein";
if($zahl%$k==0)
  {
  $teilbar="ja";
  }
if($teilbar eq "ja")
  {
  print "$zahl ist durch $k teilbar \n";
  }
======================================================================

Aufgabe: Verändere das Programm so, dass es prüft, ob $zahl eine Primzahl ist. Die Zahl darf also durch keine zahl kleiner als sie selbst teilbar sein. Man benötigt einen for-loop. Bis zu welcher Grenze hin muss Teilbarkeit geprüft werden?

Schwerere Aufgabe: Verändere das Programm so, dass $zahl die Obergrenze ist bis zu der alle Primzahlen von 2 ab berechnet werden und dann in einem array gespeichert werden.

Schwerere Aufgabe: Verändere das Programm so, dass die Teilbarkeit nur mit Primzahlen geprüft wird, das beschleunigt die Rechnung erheblich. Außerdem ist es sehr elegant, wie jetzt das array sich selber aufbaut.

Aufgabe: Füge diese Primzahlen-Funktion nun in unser Taschenrechner-Programm ein. Das ist jetzt schon ein äußerst vielseitiger, leistungsstarker Rechner!

Schwerere Aufgabe: Verändere das Programm so, dass es Primzahlzwillinge sucht, also zwei Primzahlen, die genau 2 auseinander liegen. Jetzt sind wir schon mitten im Bereich moderner Forschung: Ihr bräuchtet nur euren Computer einige Zeit laufen zu lassen und vielleicht käme eine wissenschaftliche Sensation heraus. (Genau das machen natürlich einige Menschen schon, mit sehr viel ausgeklügelteren Algorithmen auf schnellen Supercomputern...)

Kryptographie: Alles streng geheim!

Als Einstieg in die Kryptographie, die Geheimsprachen, greifen wir das Eingangsthema noch einmal auf: Das Umwandeln von Buchstaben bzw. Sätzen in ASCII-Zeichen, also Zahlen.

krypto1.pl
======================================================================
# kleine Vorübung
$str = "a";
$k = ord($str);
# die Funktion ord verwandelt einen Buchstaben in ASCII-Code
print "$k\n";
# die Funktion chr verwandelt wieder zurück
$str = chr($k);
print "$str\n";


# Jetzt das Ganze mit einem Satz
# Initialisiere einen String
$str = "Was soll denn das bedeuten?";
$laenge = length ($str);
print "$str\n";

# Umwandlung jedes einzelnen Buchstabens von in ein Array-Element und
# substr($str,$i,1)ist der i-te Buchstabe in dem String str
# Umwandlung jedes einzelnen dieser Array-Elemente mit "ord" in eine ASCII-Zahl:

for ($i=0; $i<$laenge; $i=$i+1)
  {
  @array[$i] = substr($str,$i,1);
  @array[$i] = ord(@array[$i]);
  }
print "@array\n";
# Ausgabe des Satzes in ASCII-Code
======================================================================

Aufgabe: Ändere das Programm so, dass Du folgende streng geheime Botschaft im Klartext lesen kannst. Der Satz ist jetzt schon deutlich länger als im Eingangskapitel.
Alternative: Alles per Hand übersetzen!

73 102 32 116 104 105 110 103 115 32 111 102 32 115 105 103 104 116 32 115 117 99 104 32 104 101 97 118 101 110 115 32 98 101 44 32 119 104 97 116 32 104 101 97 118 101 110 115 32 97 114 101 32 116 104 111 115 101 32 119 101 32 99 97 110 110 111 116 32 115 101 101 63

Damit ihr an dieser Stelle etwas typisch Perl-spezifisches seht, sei hier der Programm-Code eingefügt, wie man genau dasselbe mit weniger Zeilen eleganter hinbekommt:

krypto2.pl
======================================================================
# Initialisiere einen String
$str = "Was soll denn das bedeuten?";

# Zerteile den String in ein array von Buchstaben
@array = split //,$str;

# Verwandle jeden Buchstaben mit "map" und "ord" in seine ASCII-Zahl
@array = map(ord, @array);
print "@array\n";
======================================================================


Das ist natürlich noch keine echte Verschlüsselung, da der ASCII-Code ja allgemein zugänglich ist. Aber die Denkweise beim Verschlüsseln ist sehr ähnlich. Der einfachste Algorithmus mit dem früher Botschaften codiert wurden heisst Cäsar-Code und stammt eben aus dem römischen Reich. Er beruht auf dem einfachen Gedanken, alle Buchstaben um ein paar Stellen weiterzurücken. Also der Cäsar-Code zu Buchstabe C wäre folgender: Aus "a" wird "c", aus "b" wird "d", aus "c" wird "e" etc. bis zum "z, dass "b" wird.
Verstanden? Was wird aus "r"?
Damit der Code gut funktioniert, nicht zu schnell erkennbar wird, verwendet man nur Kleinschreibung und lässt die Leerstellen zwischen den Worten weg.
Der Satz "wassolldenndasbedeuten" wird dann zu "zdvvrooghqqgdvehghxwhq".

Das folgende Programm chiffriert genau nach diesem Code.

krypto3.pl
======================================================================
$abc = "abcdefghijklmnopqrstuvwxyz";
$satz = "wassolldenndasbedeuten";

$labc = length $abc;
$le = length $satz;
$shift = 3;

for ($i=0; $i<$le; $i=$i+1)
   {
   for ($j=0; $j<$labc; $j=$j+1)
    {
     if( substr($satz,$i,1) eq substr($abc,$j,1) )
       {
       $jneu = $j+$shift;
       if( $jneu>=$labc ) {
         $jneu = $jneu - $labc; 
         }
       substr($satz,$i,1) = substr($abc,$jneu,1);
       $j=$labc;
       }
     }
   } 
print "$satz\n";
======================================================================

Aufgabe: Versuche das Programm genau zu verstehen und lass es danach!! mit Perl laufen.

Aufgabe: Der folgende Text wurde mit dem Cäsar-Code um eine unbekannte Stelle verschoben. Ändere das obige Programm so, dass es dechiffrieren kann und versuche den Originaltext herauszufinden.
(Hinweis: Effiziente Programme lassen den Text gleich 26 mal durchlaufen, schieben ihn jeweils um einen der 26 Buchstaben des ABCs weiter. Danach kann man sich aus den dechiffrierten Texten den sinnvollsten auswählen)

mvezmzuzmztznzixivzwveuzvxvidrevesvzjfeeverlwxrexrlwuvddrijwvcure



Wie könnte man das verbessern? Nun, das Alphabet zum Chiffrieren muss ja nicht in der richtigen Reihenfolge stehen. Man kann "a" durch "f", "b" durch "z" und "c" durch "m" etc. ersetzen, kunterbunt durcheinander, Hauptsache ein Buchstabe wird durch einen andern ersetzt. Der Empfänger muss dann nur den Dechiffrier-Code kennen. Vorher war das nur ein einzelner Buchstabe, jetzt muss er die ganze Ordnung des Chiffrier-Alphabetes kennen, also ein Code-Wort mit 26 Buchstaben. Das muss vor Nachrichtenübermittlung zwischen Sender und Empfänger ausgetauscht werden. 

krypto4.pl
======================================================================
$abc         = "abcdefghijklmnopqrstuvwxyz";
$zufalls_abc = "sfrmdbwehcouykpzlqtigxvajn";
$satz = "wassolldenndasbedeuten";

$labc = length $abc;
$le = length $satz;

# chiffrieren, jeder Buchstabe von $satz wird durch den entsprechenden des Zufallsalphabetes ersetzt
for ($i=0; $i<$le; $i=$i+1)
  {
  for ($j=0; $j<$labc; $j=$j+1)
    {
    if( substr($satz,$i,1) eq substr($abc,$j,1) )
      {
      substr($satz,$i,1) = substr($zufalls_abc,$j,1);
      $j=$labc;
      }
    }
  } 
print "$satz\n";
======================================================================

Aufgabe: Versuche das Programm genau zu verstehen und lass es danach!! mit Perl laufen.

Aufgabe: Der folgende Text wurde mit dem obigen Programm, mit genau dem angegebenen Zufallsalphabet chiffriert. Ändere das obige Programm so, dass es dechiffrieren kann und versuche den Originaltext herauszufinden.

msthtidhktpxdqqgdroidqtsinmstoqhdwiheqkhdqsgt


Was macht nun der Spion? Wenn er das Code-Wort nicht kennt, ist er zunächst aufgeschmissen. Er kann es aber ziemlich gut herausbekommen, indem er Buchstaben zählt: In jeder Sprache tauchen die Buchstaben des Alphabetes mit charakteristischen Häufigkeiten auf. In der deutschen Sprache beispielsweise so:
E  17,40 %
N  9,78 %
S  7,58 %
I  7,55 %
R  7,00 %
A  6,51 %
T  6,15 %
D  5,08 %
H  4,76 %
U  4,35 %
L  3,44 %
C  3,06 %
G  3,01 %
M  2,53 %
O  2,51 %

Der Spion kann nun im chiffrierten Geheimtext die Buchstabenhäufigkeit ermitteln, indem er einfach zählt. Dann wird er den häufigsten Buchstaben dem "E" den zweithäufigsten dem "N" etc. zuordnen. Das folgende Programm versucht genau das.

krypto5.pl
======================================================================
# knacken von zufalls_abc

$abc = "abcdefghijklmnopqrstuvwxyz";
# das normale abc

$hauf_abc_deutsch = "ensiratdhulcgmobwfkzpvjyxq";
# das abc in der Reihenfolge der Häufigkeit in der deutschen Sprache

$hauf_abc_satz = "abcdefghijklmnopqrstuvwxyz";
# das abc in der Reihenfolge der Häufigkeit in der vorgelegten geheimen Schrift (muss noch umgeordnet werden)

$satz = "msthtidhktpxdqqgdroidqtsinmstoqhdwiheqkhdqsgt";
# der Geheimtext (je mehr Buchstaben, desto besser klappt die Dechiffrierung) 
@satz_hauf;
# ein array, dass für jeden Buchstaben ( a an erster Stelle, b an zweiter etc.) 
# die Häufigkeit zählt, mit der dieser Buchstabe in dem Geheimtext vorkommt


$labc = length $abc;
$le = length $satz;

# initialisieren
for ($j=0; $j<$labc; $j=$j+1)
   {
   $satz_hauf[$j]=0;
   } 

# zähle die Buchstaben im Geheimtext
for ($i=0; $i<$le; $i=$i+1)
   {
   for ($j=0; $j<$labc; $j=$j+1)
     {
     if( substr($satz,$i,1) eq substr($abc,$j,1) )
       {
       $satz_hauf[$j]=$satz_hauf[$j]+1;
       $j=$labc;
       }
     }
   } 

# ausgeben der Häufigkeiten
for ($j=0; $j<$labc; $j=$j+1)
   {
   print substr($abc,$j,1)." - $satz_hauf[$j]\n";
   } 

# Sortiere die Einträge im array @satz_hauf nach ihrer Größe
# parallel dazu wird auch das abc umgeordnet: Die im Geheimtext am häufigsten vorkommenden Buchstaben stehen vorn, die seltensten hinten
$ende="nein";
while($ende eq "nein")
   {
   $ende="ja";
   for($i=0;$i<$labc-1;$i=$i+1)
     {
     if($satz_hauf[$i]<$satz_hauf[$i+1])
       {
       ($satz_hauf[$i],$satz_hauf[$i+1])=($satz_hauf[$i+1],$satz_hauf[$i]);
       ($abc_array[$i],$abc_array[$i+1])=($abc_array[$i+1],$abc_array[$i]);
       $ende="nein";
       }
     }
   }

# abc neu schreiben für Satz
for ($j=0; $j<$labc; $j=$j+1)
   {
   substr($hauf_abc_satz,$j,1)=$abc_array[$j];
   } 

print "$hauf_abc_satz \n"; 
# das ist das Alphabet geordnet nach der Häufigkeit, wie die Buchstaben im Geheimsatz vorkommen
print "$hauf_abc_deutsch \n"; 
# das ist das Alphabet geordnet nach der Häufigkeit, wie die Buchstaben im Deutschen vorkommen zum Vergleich


print "$satz\n";
# Ausgabe Geheimtext original 


# ersetze jetzt noch jeden Buchstaben von $satz mit der Häufigkeit $hauf_abc_satz durch $hauf_abc_deutsch 
for ($i=0; $i<$le; $i=$i+1)
   {
   for ($j=0; $j<$labc; $j=$j+1)
     {
     if( substr($satz,$i,1) eq substr($hauf_abc_satz,$j,1) )
       {
       substr($satz,$i,1)=substr($hauf_abc_deutsch,$j,1);
       $j=$labc;
       }
     }
   } 

print "$satz\n";
# Ausgabe Geheimtext entziffert
======================================================================



Ist der Spion nun fertig? Nein, gewiss nicht, denn es wäre ein großer Zufall, wenn in dem geheimen Text die Buchstabenhäufigkeiten genau denen der gesamten deutschen Sprache entsprechen. Also wird das übersetzte Ergebnis nicht ganz deutsch ausschauen. Aber die Ausgabe des Häufigkeits-ABCs ist schon einmal eine Hilfe. Jetzt kann man durch Wörtersuche, Vergleichen von kryptischem und dechiffriertem Text das Code-abc herausbekommen und dann das umgeschriebene Programm krypto4 noch einmal anwenden, dann steht der Original-Text dechiffriert da.

Aufgabe: Der folgende Text wurde mit einem unbekannten Zufallsalphabet chiffriert. Versuche mit dem letzten Programm krypto5.pl zu ermitteln, welcher Buchstabe im Geheimtext durch welchen anderen ersetzt werden muss. Durch weiteres Kombinieren versuche mit dem Programm krypto4.pl den Originaltext herauszufinden. Den dechiffrierten Text könnt ihr ja googeln. Falls ihr den Autor und das Buch herausfindet bekommt ihr eine Tafel Schokolade.

ldhymdfylbyklnzdyadv.ynvyj.zydzsmqaytgnbglqljdpinll.esjdpiasddklmthymngpibduwdlilnnlklawnzqgndblmngdwjtyklnginywpingpibzlmdpihylpidblagpibdpilgnzlwblblzydtmsddldpiasddyn.ayntldbynzv.ywqzlmisaukmwlpvlzglesnzlmaynzdbmyddluwjzsmqqwlimblwnzkagpvblgnzgldpilgnkymlallmlljfsm.zynntgntlmlgnnypibaytlmdwpiln.gjhgmbdiywdhymjynnspihypizlmhgmbiybbluhymvlgnugjjlmuwelmjglblnyklmlmhsaablesnzljdfylblntydbylwddlmdbwlklmmydpibwnzelmhgmmbv.gnzlmhgmbddbwklywqlgnljdbmsidypvdpiayqlnayddln.v.hymzyjgblgnelmdbynzln.lgngtlkywlmnhymlnnspiklgjkglmyklmlmhsaabldgpijgbngljynzljwnblmiyablnisabldlakdbzlndbmsidypvesjzypikszlnwnzaltbldgpignzlmnylilzldsqlndign.hymjhymldzglkywlmnhymlndbgaalgnhlngtfmwlqbllmdglnspijgbzlnjwlzlnywtlnzynndpiaglqlmlgn.yklmvwmululgbzymywqhwmzllmdpisntlhlpvb.lgnxwntlmjynndbylzbgdpiyntlustlnjgbdpiywdfglalmiyqbljtldgpibzglywtlndpijyazglywtlnkmywlndbymvdbynzjgbzljhgmbnlklngij.zglkywlmnhymlnywpinspizylgngtliybblngimldlddlailmwjtlzmlibwjklddlmuwdlilnwnzuwislmln.zlmxwntlMlndpilnbdpiwazgtbldgpidlimislqagpiv.tlhlpvbuwiyklndblaabldgpiyaddsinzlddpiasddvydblaayndesmwnzdytblzynn:zgldldzsmqgdbkldgbuzlddpiasddldhlmiglmhsinbszlmwlklmnypiblbhsinbszlmwlklmnypiblbtlhgddlmjyddlngjdpiasdd.ngljynzzymqzydsinltmylqagpillmaywkngd.dglyklmiyklnlgnldsapillmaywkngdngpibszlmiyklndglhlngtdblndngpibesmtlulgtbmgmgmgmgmgmgzgzgzgzgzgzgzgzgzgzgzgzgzgbbbbbbbbbbbbbbbbzgzwzwzwzw.

Was kann der Botschafter nun tun, um seinen Geheimtext erfolgreicher zu chiffrieren? Er könnte komplexere Chiffrierverfahren mit längeren Code-Wörtern oder Primzahlen verwenden. Eine andere sehr einfache aber raffinierte Idee ist, eine möglichst seltene Sprache zum Chiffrieren zu verwenden, da die oben geschilderte Methode ja hochgradig sprachabhängig ist. Die gesamte Kommunikation des amerikanischen Geheimdienstes in Japan bei der Invasion Ende des zweiten Weltkrieges lief über den Indianer-Dialekt Navaho. Diese Codierung war äußerst erfolgreich!!

Computer creativ: Zufälle und Literatur

In gewissen Grenzen kann ein Computer auch kreativ sein. Unser Hauptbeispiel soll eine einfach sprechende Maschine werden: Ein Satzgenerator. Damit etwas Neues entstehen kann, also etwas Unvorhergesehenes werden wir uns kurz mit Zufallszahlen beschäftigen. Das ist nämlich prinzipiell die Methode, mit der Computer unvorhergesehene Wendungen z.B. in Computerspielen etc. erzeugen.

zufall.pl
======================================================================

$k = int (rand 20);
print "Zufallszahl $k";

======================================================================

Erklärung: Der Ausdruck rand 20 wählt eine zufällige Zahl aus der Menge 0 bis 20 aus (random engl.: zufällig).
Die Ergänzung int(...) rundet diese Zahl ab, so dass eine der Zahlen 0,1,2, ...,19 herauskommt.

Aufgabe: Ändere das Programm so, dass es zunächst zwei Zufallszahlen bis 30 erzeugt und diese dann miteinander multipliziert.

Aufgabe: Ändere das Programm weiter, dass es diese zwei Zufallszahlen dem Benutzer angibt und ihn nach dem Erebnis der Multiplikation fragt. Gibt der Benutzer das richtige Ergebnis an, soll ihn der Computer loben, gibt er das falsche an, sagt der Computer "schade" o.ä. Das ganze ist ale eine Art Mathe-Quiz, ein Rechenübungsprogramm, wie es auch auf der Schulhomepage steht.

Aufgabe: Ändere das Programm weiter, dass es im Falle einer falschen Ergebnisangabe des Benutzers ihn bittet es nochmal zuversuchen, mit denselben Zahlen, immer wieder, bis das richtige Ergebnis kommt.


Dieselbe Vorgehensweise kann man dazu verwenden den Computer auch sprachlich kreativ werden zu lassen. Lass folgendes Beispiel mehrmals laufen.

satz.pl
======================================================================

@artikel=("der","die","das");
@haupt=("Hund","Pferd","Taube","Kaninchen","Angela Merkel","Chuck Norris","Justin Bieber");
@verb=("rennt","liest","frisst","schimpft","schreit");
# drei verschiedene Arrays, die Wörter der jeweils gleichen Art beinhalten

$rand_artikel1=int(rand @artikel);
$artikel1=$artikel[$rand_artikel1];
$rand_haupt1=int(rand @haupt);
$haupt1=$haupt[$rand_haupt1];
$rand_verb1=int(rand @verb);
$verb1=$verb[$rand_verb1];
# es wird für jedes array eine Zufallszahl bis max Größe des arrays erzeugt
# derjenige Eintrag des arrays, den die Zufallszahl bezeichnet, wird ausgewählt

print "$artikel1 $haupt1 $verb1. \n";

======================================================================

Aufgabe: Ändere das Programm so, dass der kleine Grammatikfehler eliminiert wird. 
Das geschieht z.B. folgendermaßen: Man führt ein weiteres Array ein, @haupt_g=("m","n","f","n","f","m","m"), das das Geschlecht des Hauptwortes speichert.
Als nächstes wird dann der Artikel statt durch eine Zufallszahl, durch eine if-Abfrage ermittelt:
if(@haupt_g[$rand_haupt1] eq "m"){$rand_artikel1=0;}

Aufgabe: Füge ein Adverb in das Programm ein (schnell/laut/ruhig...).

Aufgabe: Füge ein Akkusativobjekt in das Programm ein (Die Taube frisst den Hund...).

Aufgabe: Füge weitere Objekte ein, Adjektive, Nebensätze, Ortsangaben etc. und wirf das gesamte Programm gründlich über den Haufen, bis es selbstständig einen gelungenen Roman schreiben kann.

Aufgabe: Kopiere den so entstandenen 536 Seiten langen neuen Text in eine Word-Datei, schick es als Werk der Postmoderne zum Verlag Bastei-Lübbe und verhandle dein nächstes Bestseller-Gehalt.

Aufgabe: Versuche das Programm so neu zu fassen, dass es Sätze in Reimen ausgibt: Ein Gedichtgenerator.



Diese kreativen Möglichkeiten kann man nutzen, um einen Dialog mit dem Computer in Gang zu bekommen, ganz analog zu unseren allerersten Programmen, nur etwas abwechslungsreicher. Damit die folgenden, vielleicht schwerer zu durchschauenden Programme lesbar bleiben, wurden Kommentare mit dem Symbol # eingefügt.

dialog.pl
======================================================================

@frage=("Wie geht es Dir?","Was machst Du grad?","Was ist Deine Lieblingsbeschaeftigung?");
@erwiderung=("Find ich klasse!!!","Das wuerde mir nie passieren!!!","Schaemst Du Dich nicht?","Bei mir genauso...","Das tut mir leid...");

$rand_frage=int(rand @frage);
$frage_jetzt=$frage[$rand_frage];
$rand_erwiderung=int(rand @erwiderung);
$erwiderung_jetzt=$erwiderung[$rand_erwiderung];
# es wird für jedes array eine Zufallszahl bis max Größe des arrays erzeugt
# derjenige Eintrag des arrays, den die Zufallszahl bezeichnet, wird ausgewählt

print "$frage_jetzt \n";
$antwort = <STDIN>; 
print "$erwiderung_jetzt \n";

======================================================================


Aufgabe: Schau noch einmal im ersten Kapitel nach, wie man durch if-Abfragen, die die Variable $antwort miteinbeziehen, den Dialog interessanter gestalten kann.

Aufgabe: Schau noch einmal am Anfang des dritten Kapitels nach, wie man durch einen while-loop einen endlosen Dialog gestalten kann.

Wenn es euch gelingt, dass man nicht mehr unterscheiden kann, ob ein Computer-Programm den Dialog initiiert oder ein menschlicher Teilnehmer eine Chat-Group, dann habt ihr eine Turing-Maschine, eine künstliche Intelligenz geschaffen...

Dateien: Alles gespeichert

Um dauerhaft die Ergebnisse unserer Programme zu sichern, können wir den Output leicht in eine Datei übertragen:

datei.pl
======================================================================

my $ausgabe_datei = "hierstehts.txt";
open AUSGABE, ">> $ausgabe_datei";
$satz = "hallihallo";
print AUSGABE "$satz\n";
close AUSGABE;

======================================================================

 

Rekursion: Von Viren, Zellen und digitalem Leben

Richtig lebendiges Leben ist natürlich etwas recht anderes, als die Strukturen, die am Computer entstehen. Aber manche übereinstimmungen sind schon überraschend. Wir beginnen mit einem ganz kleinen Programm, das sich selber wieder aufruft. So etwas nennt man Rekursion.

cellA.pl
======================================================================

print "hello\n";

system "perl cellA.pl";

======================================================================

Der Befehl system bedeutet nur, dass hier genau das ausgeführt wird, was in den Anführungszeichen steht. Das ist genau das, was ihr am Terminal eingebt! Was geschieht? 
Abbrechen müsst ihr mit Strg-C 
Als nächstes wollen wir, dass dieses Programm, das eine Zelle darstellen soll, sich weiter entwickelt. Dazu muss die Generation der Zellen weitergegeben werden. Damit die folgenden, vielleicht schwerer zu durchschauenden Programme lesbar bleiben, wurden Kommentare mit dem Symbol # eingefügt.

cellB.pl
======================================================================

# die Nummer der Mutterzelle wird beim Programmaufruf uebergeben
$cellnumber = @ARGV[0];

# die Nummer wird um eins erhoeht
$cellnumber += 1;

print "Hello, I am cell number $cellnumber\n";

system "perl cellB.pl $cellnumber";

======================================================================

Wenn es nicht durch Strg-C abgebrochen wird, läuft das Programm ewig und erzeugt virtuell neue Zellen. Das ist der erste kleine Schritt in Richtung Leben. Hier haben wir ein Leben, das ewig fortdauert, indem es sich selbst immer wieder aktiviert. Informationstechnisch bezeichnet man eine Funktion, die sich selbst wieder aufruft als "rekursiv". 
Der nächste größere Schritt ist die Vermehrung.

cellC.pl
======================================================================

# die Nummer der Mutterzelle wird beim Programmaufruf uebergeben
$cellnumber = @ARGV[0];

# die Nummer wird um eins erhoeht
$cellnumber += 1;

print "Hello, I am cell number $cellnumber\n";

system "perl cellC.pl $cellnumber &";
system "perl cellC.pl $cellnumber &";

======================================================================

Was würde man erwarten, wenn man das obenstehende Programm laufen lässt?
Was geschieht in Wirklichkeit?
Also, es scheint doch etwas schwieriger zu sein. Um zu verstehen was hier passiert, muss man ein wenig in die Vorgehensweise eines Computers hineinschauen. Das Perl-Programm ist für den Computer ein Prozess, den er Schritt für Schritt abarbeitet. Kommt er nun an den Befehl system "perl cellC.pl $cellnumber &"; wartet er, bis der Befehl bis zu Ende ausgeführt ist. Da aber das Programm "cellC.pl" gleich wieder "cellC.pl" aufruft, kommt dieser Prozess nie zu Ende und der zweite "perl cellC.pl $cellnumber &"-Aufruf wird nie erreicht. Damit das Programm die beiden "perl cellC.pl $cellnumber &"-Aufrufe gleichzeitig betreiben kann, muss es zwei grundsätzlich neue, unabhängige Prozesse ins Leben rufen. Das nennt man Multitasking. Jetzt wird es also schon ein wenig anspruchsvoller.

cellD.pl
======================================================================

# die Nummer der Mutterzelle wird beim Programmaufruf uebergeben
$cellnumber = @ARGV[0]; 

# die Nummer wird um eins erhoeht 
$cellnumber += 1; 
print "Hello, I am cell number $cellnumber\n";

# Achtung; Hier ist das Neue: Der Prozess verzweigt sich (fork)
# und selbststaendig laufen zwei neue Programme los
# der Nachsatz "die" (sterben) ist nur fuer den Fall, dass etwas schief geht
defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {
   system "perl cellD.pl $cellnumber &" or die "system call not possible";
   }

defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {
   system "perl cellD.pl $cellnumber &" or die "system call not possible";
   }

======================================================================

Wenn das Programm jetzt läuft, erzeugt es zwei unabhängige Prozesse, die ihrerseits wieder zwei unabhängige Prozesse erzeugen und diese auch jeweils wieder, so dass man 2, 4, 6, 16 Zellverhmehrungen hat: Die Anzahl der Prozess-Zellen wächst exponentiell! 

Als nächstes sollen diese Zellvermehrungen auch auf der Hardware stattfinden. Wie ein physischer Virus. Dafür erlauben wir dem Programm Festplattenzugriff. Das führt ganz schnell dazu, dass das Programm ein wenig technisch wird, schwerer lesbar. Das ist leider das Wesen aller korrekt funktionierender Programme. 

cellE.pl
======================================================================

use File::Copy;

# Name der Zellkultur, da wir inzwischen verschiedene Generationen haben
$cell_name = "cellE";

# Die Urzelle aller Zellen
$motherfile = $cell_name.".pl";

# die Mutterzelle lebt weiter, in der 1.Verzweigung (fork)
# der Nachsatz "die" (sterben) ist nur für den Fall, das computertechnisch etwas schiefgeht
defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {
   system "perl $motherfile &" or die "cannot start motherfile";
   }

# Die Zellteilung (fork zur 2.Verzweigung)
defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {

# check wie viele Zellen es schon gibt, hierfür werden die vorangegangenen Dateien kurz geoeffnet
# dieser Schritt wird einige verwundern, geht das denn nicht leichter?
# nicht wirklich, wenn man moechte, dass jede einzelne Zelle autark ist,
# d.h. es soll keine Superzelle geben, die alles im Blick hat und Zellennummern vergeben kann etc.
   $cellnumber = 0;
   $newfile = $motherfile;
   while (open(DATEI, $newfile)) {
     $cellnumber += 1;
     $newfile=$cell_name.$cellnumber.".pl";
     close(DATEI);
     }

# Der neue Name ist gefunden und die Mutterzelle wird verdoppelt (copy) und Kundgegeben (print)
# als obere Schranke gegen Festplattenschaeden und fuer korrekte Zaehlweise ist 1000 eingefuegt
   if ($cellnumber<1000)
     {
     copy($motherfile, $newfile);
     print $newfile."\n";

# Mit Erzeugen und Kundgeben der neuen Zelle ist es nicht getan:
# Sie muss auch zum Leben erweckt werden, der Nachsatz "die" (sterben) ist wieder nur für den Fall, das was schiefgeht
     system "perl $newfile &" or die "cannot start newfile";
     }
   }

======================================================================

Hier jetzt die vorerst letzte Version, die die Dateinamen/-nummerierung der Zellen etwas geschickter handhabt, so dass sie alphabetisch erfolgt und die letzte Zellennummer schneller gefunden werden kann. Damit kann man nun sehr schön im ProgramManager die Vermehrung der Zellen mitverfolgen. Ansonsten ist es eine Kopie des letzten Programmes: Eine exponentielle Zellvermehrung auf der Festplatte.

cellF.pl
======================================================================

use File::Copy;

# die Nummer dieser Zelle selber wird beim Programmaufruf uebergeben
# Man muss ab jetzt also unterscheiden zwischen Urmutterzelle und direkter Abstammungszelle

$oldcellnumber = @ARGV[0]; 

$cell_name = "cellF"; 

# die Urmutterzelle hat keine Nummer
# die Nachkommen haben Nummern, mit Nullen angefüllt damit mit den Zahlen eine alphabetische Reihenfolge entsteht
if (!($oldcellnumber>0)) {
   $cellnumberstr = ""; 
   } 
else {
   $cellnumberstr = "000"; 
   substr($cellnumberstr,(3-length($oldcellnumber)),3) = "$oldcellnumber";
   }
$motherfile = $cell_name.$cellnumberstr.".pl";

# die direkte Mutterzelle lebt weiter, in der 1.Verzweigung (fork)
# der Nachsatz "die" (sterben) ist nur für den Fall, das was schiefgeht
defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {
   system "perl $motherfile &" or die "cannot start $motherfile";
   }

defined(my $pid = fork) or die "fork not possible: $!";
unless ($pid) {

# check wie viele Zellen es schon gibt
# Das Array "cells" enthält die Dateinamen aller Dateien von der Form "cellF..." stehen (Befehl glob)
# Die Anzahl der Dateien in diesem Array ist "@cells", das ist dann die Nummer der neuen Zelle
# als obere Schranke gegen Festplattenschaeden und fuer korrekte Zaehlweise ist 1000 eingefuegt
   my @cells = glob $cell_name."*.pl"; 
   $cellnumber = @cells;
   if ($cellnumber<1000)
     {
     $cellnumberstr = "000"; 
     substr($cellnumberstr,(3-length($cellnumber)),3) = "$cellnumber";
     $newfile=$cell_name.$cellnumberstr.".pl";

# Der neue Name ist gefunden und die Mutterzelle wird verdoppelt (copy) und Kundgegeben (print)
     copy($motherfile, $newfile); 
     print $newfile."\n";

# Mit Erzeugen und Kundgeben der neuen Zelle ist es nicht getan:
# Sie muss auch zum Leben erweckt werden, der Nachsatz "die" (sterben) ist nur für den Fall, das was schiefgeht
     system "perl $newfile $cellnumber &" or die "cannot start cell $cellnumber";
     }
   }

======================================================================

Für einen Virus wäre das bisher Geschriebene schon ausreichend, ausser, dass die Datei noch besser verpackt, d.h. unkenntlich gemacht werden müsste und an eine email oder ein Word-Programm angehängt werden müsste. Für die Programmierer virtueller Leben kommt aber noch ein weiterer wichtiger Schritt dazu, der das Ganze erst richtig spannend macht.

Während die Zellen sich munter vermehren, startet man ein zweites Programm, (ich habe es predator.pl genannt). Dieses Programm schaut, ob es lebende Zellen gibt und frisst sie sodann, indem es die Zell-Datei löscht. Wenn es dann eine Zelle gefressen (d.h. die Datei gelöscht) hat, kann das neue Programm Nachkommen bilden, d.h. sich ebenfalls vermehren. Diese Vermehrung wird exponentiell fortschreiten, solange dieses Räuber-Programm etwas zu fressen findet. Sobald es keine Zell-Datei findet, oder zu wenige, wird es seinerseits sterben. So beginnt ein permanentes Erzeugen und Fressen von Dateien auf der Festplatte. Den Code für das Räuber-Programm müsst Ihr nach dieser Beschreibung selber entwerfen. Es sieht sehr ähnlich wie cellF.pl aus. Der entscheidende Einschub ist in etwa wie folgt:


my @cells = glob $cell_name."*.pl"; 
$cellnumber = @cells;
# mindestens 10 Zellen muss der Raeuber sehen, um eine zum Fressen zu erwischen
if ($cellnumber>10)
   {
# bilde Zufallszahl aus Zellenmenge, um eine Zelle zum Fressen auszuwaehlen
   $rand_cell=int(rand (@cells-1)); 
   $prey = $cells[$rand_cell];
   print "eat ".$prey."\n";
# die zufaellig ausgwaehlte Zelle ($prey) wird gefressen, sprich ihre Datei wird geloescht ("unlink")
   unlink ($prey) or die "digestion problem"; 
   }

Falls Euch das Programm gelingt ... alle Achtung, jetzt habt ihr es schon weit gebracht. Dieses ständige Beschreiben und Löschen von Dateien in einem Verzeichnis ist vielleicht nicht gerade heilsam für die Festplatte, aber es ist sehr spannend zeitgleich von Außen mit dem Datei-Manager diese Entwicklung von entstehenden und vergehenden "Zellen" (Dateien) zu beobachten. Man nennt so etwas Räuber-Beute-System. Solche Systeme werden von der Lotka-Volterra-Gleichung beschrieben. Vorhersagbarkeit und Unvorhersagbarkeit solcher Systeme / Biotope sind vieldiskutierte Themen in der heutigen Wissenschaft.  

Spiele

Zunächst als Vorbereitung, nur eine Art graphische Ausgabe:

tictactoe.pl
======================================================================

print "_____________\n";
print "|   |   |   |\n";
print "| 1 | 2 | 3 |\n";
print "|___|___|___|\n";
print "|   |   |   |\n";
print "| 4 | 5 | 6 |\n";
print "|___|___|___|\n";

print "\n";

======================================================================


Aufgabe: Vervollständige die Zahlen so, dass ein Quadrat 1 - 9 entsteht.

Jetzt beginnt das eigentliche Spiel, wo man als Spieler auch eigene Eingaben machen kann. Beachte, dass die Arrays bei 0 anfangen, wohingegen die normale Zählweise mit 1 beginnt. Damit das Programm lesbar bleibt, wurden Kommentare mit dem Symbol # eingefügt.

tictactoe.pl
======================================================================

@mark=(' ','X','O','X',' ','O',' ',' ');
# das sind nur Markierungen, um zu zeigen, wie es prinzipiell geht
# eigentlich muss bei Spielbeginn das Spielfeld natürlich leer sein
$gameEnd = 0;
while ($gameEnd==0) 
   {
# zuerst wird die Nummerierung angezeigt
   print "_____________\n";
   print "|   |   |   |\n";
   print "| 1 | 2 | 3 |\n";
   print "|___|___|___|\n";
   print "|   |   |   |\n";
   print "| 4 | 5 | 6 |\n";
   print "|___|___|___|\n";


   print "\n";
   print "\n";

# dann wird der momentane Spielstand angezeigt
   print "_____________\n";
   print "|   |   |   |\n";
   print "| ".$mark[0]." | ".$mark[1]." | ".$mark[2]." |\n";
   print "|___|___|___|\n";
   print "|   |   |   |\n";
   print "| ".$mark[3]." | ".$mark[4]." | ".$mark[5]." |\n";
   print "|___|___|___|\n";
   print "\n";
   print "\n";
   print "Spieler Kreuz: Welches Feld (1-9) soll markiert werden? (q=quit)\n";
   $antwort = <STDIN>; 
   chomp($antwort);
   if ($antwort eq "q")
     {
     $gameEnd=1; 
     }
   for ($i=0; $i<9; $i=$i+1)
     {
     if ($antwort==$i)
       {
       $mark[$i-1]='X';
       }
     }
   }

======================================================================

Aufgabe: Vervollständige das Spiel weiter, dass nach dem X ein zweiter Spieler ein O eingeben kann.

Aufgabe: Vervollständige das Spiel weiter, dass nach der Eingabe zunächst geprüft wird, ob auf dem Feld der eingegebenene Nummer schon bereits ein X oder ein O eingezeichnet ist.

Aufgabe: Vervollständige das Spiel weiter, dass eine Gewinnmeldung ausgegeben wird, wenn eine Reihe von drei X oder O erreicht wurde. In diesem Fall ist auch das Spiel zu Ende.

Aufgabe: Vervollständige das Spiel weiter, dass es eine Unentschiedenmeldung ausgibt und Spielende anzeigt, wenn alle Felder ausgefüllt sind.

Aufgabe für Ehrgeizige: Versuche die O Einträge vom Computer selber machen zu lassen, d.h. entwickle eine Strategie, wie das beste O gesetzt wird, nachdem der erste Spieler ein Kreuz gesetzt hat!

Aufgabe: Vervollständige das Spiel weiter, indem es auf ein 4x4 Spielfeld erweitert wird. 

Waldorfschule Ismaning

Die Waldorfschule Ismaning bei München besteht seit 1986 als Alternative zu staatlichen Regelschulen. Heute besuchen ca. 400 Schülerinnen und Schüler die Klassen 1-13. Unsere Kinder und Jugendlichen werden in ihrer gesamten Persönlichkeit gefördert und über den Waldorfabschluss hinaus bis zur staatlichen Mittleren Reife bzw. zum Abitur geführt.

Schulbüro

Wir sind für Sie da:

Free Joomla! templates by AgeThemes