15 KiB
language | contributors | translators | filename | lang | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Tcl |
|
|
learntcl-de.tcl | de-de |
Tcl wurde kreiert von [John Ousterhout](http://wiki.tcl.tk/John Ousterout) als eine wiederverwendbare Scriptsprache für Chip-Design Werkzeuge die er kreiert hat. Im Jahre 1997 wurde er mit dem ACM Software System Award für Tcl ausgezeichnet. Tcl kann sowohl als eingebettete Scriptsprache als auch als allgemeine Programmier-Sprache verwendet werden. Tcl kann auch als portable C-Bibliothek verwendet werden. Sogar in Fällen in denen die Script-Fähigkeiten nicht nötig sind. Denn Tcl stellt Daten-Strukturen wie dynamische Zeichenketten, Listen und Hash-Tabellen bereit. Die C-Bibliothek stellt auch portable Funktionen zur Verfügung: Laden von dynamischen Bibliotheken, Zeichenketten Formatierung und Codekonvertierung, Dateisystemoperationen, Netzwerkoperationen und mehr.
Verschiedenste herausragende Fähigkeiten von Tcl:
-
Praktische Cross-Platform Netzwerk-API
-
Vollständig virtualisiertes Dateisystem
-
Stapelbare I/O Kanäle
-
Asynchron bis zum Kern
-
Vollständige Ko-Routinen
-
Robustes und einfach zu verwendendes Thread-Modell
Wenn Lisp ein Listen-Prozessor ist, dann ist TCl ein Zeichenketten-Prozessor. Alle Werte sind Zeichenketten. Eine Liste ist ein Zeichenketten-Format. Eine Prozedur-Definition ist ein Zeichenketten-Format. Um leistungsfähig zu sein, werden Tcl-intern diese Zeichenketten in Strukturierter-Form gepuffert. Ein Beispiel: Der "list" Befehl arbeitet mit diesen internen gepufferten Repräsentationen. Tcl kümmert sich selbständig darum die String-Repräsentationen zu aktualisieren, falls dies im Skript benötigt werden sollten. Das Kopieren- beim-Schreiben-Design von Tcl erlaubt es Skript-Autoren mit großen Daten- Strukturen zu arbeiten ohne zusätzlichen Speicher-Overhead. Prozeduren werden automatisch byte-kompiliert außer sie verwenden dynamische Befehle wie zum Beispiel "uplevel", "upvar und "trace".
Es ist eine Freude in Tcl zu programmieren. Hacker-Typen werden gefallen daran finden, wenn sie Lisp, Forth oder Smalltalk interessant finden. Tcl wird auch Ingenieuren und Wissenschaftlern gefallen die nur den Job erledigen wollen, und zwar mit Werkzeugen die sich ihrem Willen anpassen. Bei Tcl ist jegliche Funktionalität in Befehlen ausgeführt, selbst Dinge wie Schleifen und Mathematische-Funktionen die bei anderen Sprachen normalerweise Teil der Syntax sind. Das erlaubt Tcl in den Hintergrund von Domänen spezifischen Sprachen zu treten die das jeweilige Projekt gerade benötigt. Die Tcl-Syntax ist sehr leichtgewichtig. Sie ist selbst leichtgewichtiger als die Syntax von Lisp. Tcl steht dir einfach nicht im Weg.
#! /bin/env tclsh
################################################################################
## 1. Richtlinien
################################################################################
# Tcl ist nicht Bash oder C! Das muss gesagt werden, denn standard Shell-Quoting
# funktioniert fast mit Tcl. Daher glauben viele sie können diese Syntax für
# Tcl übernehmen. Am Beginn funktioniert das meist, führt aber schnell zu
# Frustrationen wenn die Skripte komplexer werden.
# Eckige-Klammern sind nur Quoting-Mechanismen, keine Code-Block-Konstruktoren
# und auch keine Listen-Konstruktoren. In Tcl gibt es diese beiden Dinge nicht.
# Eckige-Klammern werden verwendet um Spezial-Zeichen in Prozeduren zu escapen
# und in Zeichenketten die als Listen formatiert sind.
################################################################################
## 2. Syntax
################################################################################
# Jede Zeile ist ein Befehl. Das erste Wort ist der Name des Befehls, jedes
# weitere Wort ist ein Argument des Befehls. Wörter sind begrenzt durch
# Leerzeichen. Da jedes Wort auch ein String ist, sind keine speziellen
# Auszeichnungen wie Anführungs-Zeichen, Klammern oder Backslashes nötig.
# Selbst wenn Anführungs-Zeichen verwendet werden, denn sie sind ja keine
# String-Konstruktoren, sondern nur Escape-Zeichen.
set greeting1 Sal
set greeting2 ut
set greeting3 ations
# Strichpunkte begrenzen auch Befehle
set greeting1 Sal; set greeting2 ut; set greeting3 ations
# Das Dollar-Zeichen zeigt eine Variablen-Substitutionen an.
set greeting $greeting1$greeting2$greeting3
# Eckige-Klammern zeigen Befehls-Substitutionen an. Das Ergebnis des Befehls wird an
# Stelle des Klammern-Ausdrucks eingefügt. Wenn man dem "set" Befehl nur den
# Namen einer Variablen übergibt, gibt er den Wert der Variablen zurück.
set greeting $greeting1$greeting2[set greeting3]
# Befehls-Substitution sollte eigentlich Script-Substitution heißen, denn ein
# komplettes Script, und nicht nur ein Befehl, kann zwischen die Eckigen-Klammern
# geschrieben werden. Der "incr" Befehl erhöht den Wert einer Variable um 1
# und gibt den neuen Wert der Variable zurück.
set greeting $greeting[
incr i
incr i
incr i
]
# Der Backslash unterdrückt die Bedeutung von Sonderzeichen
set amount \$16.42
# Der Backslash macht bestimmte Zeichen zu Sonderzeichen
puts lots\nof\n\n\n\n\n\nnewlines
# Ein Wort das in geschweiften Klammern eingeschlossen wurde ist von jeglichen
# speziellen Interpretationen ausgeschlossen. Eine Ausnahme bilden Backslashes
# vor geschweiften Klammern, hiermit wird die geschweifte Klammer von der Suche
# nach der schließenden geschweiften Klammer ausgeschlossen.
set somevar {
Das ist ein literales $ Zeichen, diese geschweifte Klammer \} wird nicht
als Ende interpretiert.
}
# Bei einem Wort das in doppelten Anführungszeichen steht verlieren Leerzeichen
# ihre spezielle Bedeutung.
set name Neo
set greeting "Hallo, $name"
#Variablen-Namen können irgend eine Zeichenkette sein.
set {first name} New
# Die Geschweifte-Klammern-Form der Variablen-Substitution kann sehr komplexe
# Variblen-Namen handhaben.
set greeting "Hello, ${first name}"
# Der "set" Befehl kann immer anstatt einer Variablen-Substition verwendet
# werden.
set greeting "Hello, [set {first name}]"
# Mit dem Expansions-Operator "{*}" werden Wörter innerhalb eines Wortes wieder
# individuell als Teile des aktuellen Befehls behandelt.
set {*}{name Neo}
# Ist Äquivalent zu
set name Neo
# Ein Array ist eine spezielle Variable die also Container für andere Variablen
# dient.
set person(name) Neo
set person(gender) male
set greeting "Hello, $person(name)"
# Ein Namensraum enthält Befehle und Variablen
namespace eval people {
namespace eval person1 {
variable name Neo
}
}
#Der volle Name einer Variablen beihaltet den/die umschließenden
# Namensraum/Namensräume begrenzt durch zwei Doppelpunkte.
set greeting "Hello $people::person1::name"
################################################################################
## 3. Einige Notizen
################################################################################
# Jede weitere Funktion ist über Befehle implementiert. Von nun an kommt keine
# neue Syntax hinzu. Alles weitere das es über Tcl zu lernen gibt ist das
# Verhalten individueller Befehle und die bedeutung ihrer Argumente.
# Um einen Interpreter zu bekommen mit dem man nichts mehr machen kann, lösche
# einfach den globalen Namensraum. Das ist nicht sehr sinnvoll, zeigt aber die
# Natur von Tcl.
namespace delete ::
# Wegen des Verhaltens der Namens-Auflösung ist es sicherer den "variable"
# Befehl zu verwenden um in einem Namensraum einen Wert zu deklarieren oder
# zuzuweisen. Wenn eine Variable mit dem Namen "name" bereits im globalen
# Namensraum existiert, bewirkt der "set" Befehl das der globalen Variable ein
# Wert zugewiesen wird, anstatt eine Variable im lokalen Namensraum zu erzeugen
namespace eval people {
namespace eval person1 {
variable name Neo
}
}
# Es kann immer der vollständige Name einer Variable verwendet werden, falls
# gewünscht.
set people::person1::name Neo
################################################################################
## 4. Befehle
################################################################################
# Berechnungen werde mit dem "expr" Befehl durchgeführt.
set a 3
set b 4
set c [expr {$a + $b}]
# Since "expr" performs variable substitution on its own, brace the expression
# to prevent Tcl from performing variable substitution first. See
# Da der "expr" Befehl eigene Variablen-Substitutionen durchführt, setze den
# zu berechnenden Ausdruck in Eckige-Klammern. Das hindert Tcl daran Variablen-
# Substitutionen durchzuführen. Für Details siehe:
# "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions"
# Der "expr" Befehl versteht Variablen- und Befehls-Substitutionen
set c [expr {$a + [set b]}]
# Der "expr" Befehl stellt Mathematische-Funktionen zur Verfügung.
set c [expr {pow($a,$b)}]
# Mathematische Operatoren sind als Befehle auch im Namensraum
# ::tcl::mathop verfügbar.
::tcl::mathop::+ 5 3
# Befehle können aus anderen Namensräumen importiert werden.
namespace import ::tcl::mathop::+
set result [+ 5 3]
# Neu Befehle werden mit dem "proc" Befehl gebildet.
proc greet name {
return "Hello, $name!"
}
#Es können mehrere Parameter spezifiziert werden.
proc greet {greeting name} {
return "$greeting, $name!"
}
# Wie bereits erwähnt, geschwungene Klammern erzeugen keinen Code-Block.
# Jeder Wert, sogar das dritte Argument für den "proc" Befehl ist eine
# Zeichenkette. Der vorherige Befehl kann daher auch ohne
# geschwungene Klammern geschrieben werden:
proc greet greeting\ name return\ \"Hello,\ \$name!
# Wenn der letzte Parameter der literale Wert "args" ist, sammelt dieser Wert
# alle übrigen Argumente des Befehls ein wenn dieser aufgerufen wird.
proc fold {cmd args} {
set res 0
foreach arg $args {
set res [$cmd $res $arg]
}
}
fold ::tcl::mathop::* 5 3 3 ;# -> 45
# Bedingte Ausführung ist auch als Befehl implementiert
if {3 > 4} {
puts {This will never happen}
} elseif {4 > 4} {
puts {This will also never happen}
} else {
puts {This will always happen}
}
# Auch Schleifen sind Befehle. Das erste, zweite und dritte Argument des "for"
# Befehls wird als mathematischer Ausdruck behandelt.
for {set i 0} {$i < 10} {incr i} {
set res [expr {$res + $i}]
}
# Das erste Argument des "while" Befehls wird auch als mathematischer Ausdruck
# behandelt.
set i 0
while {$i < 10} {
incr i 2
}
# Eine Liste ist eine speziell formatierte Zeichenkette. Im einfachsten Fall
# genügen Leerzeichen als Trennzeichen zwischen den einzelnen Werten.
set amounts 10\ 33\ 18
set amount [lindex $amounts 1]
# Geschwungene Klammern und Backslashes können verwendet werden um komplexe
# Werte in einer Liste zu formatieren. Eine Liste sieht aus wie ein Skript,
# allerdings verlieren Zeilenumbrüche und Doppelpunkte ihre
# besondere Bedeutung. Diese Funktionalität macht Tcl homoikonisch. Die
# folgende Liste enthält drei Elemente.
set values {
one\ two
{three four}
five\{six
}
# Da Listen auch Zeichenketten sind, kann man Zeichenketten-Operationen auf
# ihnen anwenden. Allerdings mit dem Risiko die Formatierung der Liste zu
# beschädigen.
set values {one two three four}
set values [string map {two \{} $values] ;# $values is no-longer a \
properly-formatted listwell-formed list
# Der sicherste Weg korrekt formatierte Liste zu erzeugen, ist den "list"
# Befehl zu verwenden.
set values [list one \{ three four]
lappend values { } ;# Ein Leerzeichen als Element der Liste hinzufügen
# Mit "eval" können Werte als Skripts evaluiert werden.
eval {
set name Neo
set greeting "Hello, $name"
}
# Eine Liste kann immer an "eval" übergeben werden, solange die Liste einen
# einzigen Befehl enthält.
eval {set name Neo}
eval [list set greeting "Hello, $name"]
# Daher: Wenn "eval" verwendet wird, verwende [list] um den gewünschten Befehl
# aufzubauen.
set command {set name}
lappend command {Archibald Sorbisol}
eval $command
# Es ist ein häufiger Fehler die Listenfunktionen beim Aufbauen von Listen
# nicht zu verwenden.
set command {set name}
append command { Archibald Sorbisol}
eval $command ;# Hier passiert eine Fehler, denn der "set" Befehl hat nun zu \
viele Argumente {set name Archibald Sorbisol}
# Dieser Fehler kann auch leicht beim "subst" Befehl passieren.
set replacement {Archibald Sorbisol}
set command {set name $replacement}
set command [subst $command]
eval $command ;# The same error as before: too many arguments to "set" in \
{set name Archibald Sorbisol}
# Die korrekte Vorgangsweise ist es den substituierten Wert mit dem "list"
# Befehl zu formatieren.
set replacement [list {Archibald Sorbisol}]
set command {set name $replacement}
set command [subst $command]
eval $command
# Der "list" Befehl wird sehr häufig verwendet um Werte zu formatieren die
# in Tcl Skript Vorlagen substituiert werden. Es gibt dazu viele Beispiele,
# siehe unterhalb.
# Der "apply" Befehl evaluiert eine Zeichenkette als Befehl.
set cmd {{greeting name} {
return "$greeting, $name!"
}}
apply $cmd Whaddup Neo
# Der "uplevel" Befehl evaluiert ein Skript in einem höher liegenden
Gültigkeitsbereich.
proc greet {} {
uplevel {puts "$greeting, $name"}
}
proc set_double {varname value} {
if {[string is double $value]} {
uplevel [list variable $varname $value]
} else {
error [list {not a double} $value]
}
}
# Der "upvar" Befehl verknüpft eine Variable im aktuellen Gültigkeitsbereich
# mit einer Variable in einem höher liegenden Gültigkeitsbereich.
proc set_double {varname value} {
if {[string is double $value]} {
upvar 1 $varname var
set var $value
} else {
error [list {not a double} $value]
}
}
# Werde den eingebauten "while" Befehl los.
rename ::while {}
# Definieren einen neuen "while" Befehl mithilfe des "proc" Befehls.
# Ausführlichere Fehler-Behandlung wird dem Leser als Übung überlassen.
proc while {condition script} {
if {[uplevel 1 [list expr $condition]]} {
uplevel 1 $script
tailcall [namespace which while] $condition $script
}
}
# Der "coroutine" Befehl erzeugt einen separaten Call-Stack, zusammen mit einem
# Befehl um diesem Call-Stack zu verwenden. Der "yield" Befehl unterbricht
# die Ausführung des aktuellen Call-Stacks.
proc countdown {} {
#send something back to the initial "coroutine" command
yield
set count 3
while {$count > 1} {
yield [incr count -1]
}
return 0
}
coroutine countdown1 countdown
coroutine countdown2 countdown
puts [countdown 1] ;# -> 2
puts [countdown 2] ;# -> 2
puts [countdown 1] ;# -> 1
puts [countdown 1] ;# -> 0
puts [countdown 1] ;# -> invalid command name "countdown1"
puts [countdown 2] ;# -> 1