Version 32 of Einfach Tcl

Updated 2001-11-21 15:11:18

Richard Suchenwirth - This page ("simply Tcl") is in German for even more i18n;-) It is the draft of a planned presentation. First thought of ppt or Word, but somehow I preferred the Wiki - plus allowing at least German speakers from Tclworld to discuss this while I'm writing it - best place your comments etc. at bottom of page!


Tcl (Tool command Language, ausgesprochen "tickel") ist eine einfache, aber m�chtige Programmiersprache. Da Turing-vollst�ndig, kann im Prinzip jede Aufgabe in Tcl gel�st werden, jedoch nicht immer optimal.

Tcl wird (zusammen mit Perl, Python, VB) auch als Scriptingsprache bezeichnet. Zu "System-Programmiersprachen" wie C, Java, Assembler bestehen folgende Unterschiede:

  • nahezu typfrei ("Everything is a string")
  • Variablen m�ssen nicht deklariert werden - automatische Speicherverwaltung
  • Ausf�hrung durch Interpreter (byte-kompilierte Prozeduren)
  • Interaktive Nutzung m�glich, reichhaltige Introspektionsm�glichkeiten
  • Aussagekr�ftige Fehlermeldungen (fast "Online-Hilfe"); Weiterarbeit m�glich
  • Compile-Link-Zyklus entf�llt, daher schnelle Entwicklung
  • Wartbarkeit durch Script-Editieren bis zur Zielmaschine
  • Laufzeitverhalten teilweise schlechter als vollkompilierter Code

Tcl kann mit C/C++/Java-Code f�r laufzeitkritische Teile auf verschiedene Weisen verbunden werden:

  • Einbettung (Applikation enth�lt Tcl-Interpreter)
  • Erweiterung (statischer oder dynamisch gelinkter Code wird f�r Tcl zug�nglich gemacht)
  • Tcl_LinkVar: Bindung von Tcl-Variablen an C-Variablen (int, double, boolean, string)
  • Tcl_CreateObjCommand: Bindung eines Tcl-Kommandos an eine C-Implementation
  • externe Server (namenlose bidirektionale Pipes)

Tk (ToolKit) ist die wichtigste Tcl-Erweiterung, ein plattformunabh�ngiger GUI-Toolkit (X11, Windows, Macintosh):

  • extrem einfache GUI-Spezifikation
  • gleiche Quelldatei l�uft auf mehreren Plattformen mit "native look & feel"
  • Leistungsf�hige "Widgets": canvas, text, menu, scrollbar, Buttons...
  • Einfache aber m�chtige Geometrie-Manager: pack, place, grid
  • Tk wird sogar von Perl und Python als GUI-Toolkit verwendet

Weitere bekannte Erweiterungen:

  • [incr Tcl] - OO-Unterst�tzung �hnlich C++
  • Expect - Unterst�tzung von Kommunikation zwischen verschiedenen Rechnern, "Fernsteuerung" von Applikationen
  • BWidget - Erweiterung von Tk um komplexere "Megawidgets" in purem Tcl
  • TclX - Erweiterung um Posix-Systemfunktionalit�ten

Tcl bietet starke Unterst�tzung von Internationalisierung mit Unicode:

  • alle Strings intern in Unicode/UTF-8
  • Stringkonstanten mit \uxxxx k�nnen jeden Unicode darstellen
  • Hin- und Herkonvertierung in viele andere internationale Encodierungen
  • Tk: automatische Zeichensuche, falls der aktuelle Font ein gew�nschtes Zeichen nicht enth�lt
  • Einfache Lokalisierung mit msgcat

Tcl vereint Konzepte aus verschiedenen Software-Welten:

  • LISP/Scheme: Liste als zentrale Datenstruktur, polnische Notation, Code = Daten
  • C: Kontrollstrukturen (for, while, if); Arithmetik
  • Shells: Trennung von Parsing und Ausf�hrung; cd, pwd, glob...
  • SNOBOL/awk: assoziative Arrays (Hashtables)
  • X/...: Eventmodell, ereignisgesteuerte Verarbeitung
  • ?: Traces - Notifikation bei Lesen/Setzen/L�schen einer Variablen

Tcl hat die einfachste Syntax aller Scriptingsprachen:

  • keine Schl�sselw�rter, alle Kommandos werden gleich behandelt
  • Script: Folge von Kommandos, durch \n oder ";" getrennt
  • Kommando: Folge von W�rtern, durch Whitespace getrennt; erstes Wort ist Kommandoname
  • Wort: whitespacelose Zeichenkette, oder gruppiert mit ".." {..}
  • "..": "substituiere und gruppiere mich zu einem Wort"
  • {..}: "gruppiere mich, aber r�hr meinen Inhalt nicht an"
  • Rekursiver Interpreteraufruf f�r [..]-eingebettete Scripts
  • [..]: "evaluiere mich zuerst zu einem String"
  • Variablensubstitution durch $name , Array-Elemente: $name($key)
  • Namensraum-Syntax: ::foo::bar; :: ist der globale Namensraum
  • Alle weitere Syntax liegt in Verantwortung der Kommandos

Kommandos haben folgende Eigenschaften:

  • Implementation in C oder als Tcl-proc
  • Argumente mit Defaultwert oder variabler Anzahl m�glich
  • jedes Kommando parst seinen Input nach eigenem Bedarf
  • Kommandos k�nnen umbenannt und �berladen werden
  • Bytecompilation beim ersten Aufruf, danach Wiederverwendung des Bytecodes
  • Neudefinition zur Laufzeit m�glich (proc ist auch nur ein Kommando)
  • auch Kontrollstrukturen sind "nur" Kommandos, k�nnen �berladen oder erweitert werden
  • alle Variablen von Procs sind lokal, k�nnen aber mit globalen Variablen oder Variablen h�her im Callstack verbunden werden

Strukturierung von Tcl-Applikationen:

  • Auch in Tcl kann man unwartbaren Code schreiben, mu� es aber nicht
  • Explizites Laden anderer Scripte mit source (#include zur Laufzeit)
  • Automatisches Laden anderer Scripte bei Bedarf (auto_index)
  • Modularisierung durch ladbare packages (Script und/oder kompiliert)
  • Daten- und Prozedurkapselung durch Namensr�ume

Datenhaltung in Tcl:

  • Listen mit automatischer Speicherverwaltung
  • Assoziative Arrays (Hashtables): Schl�ssel -> Wert
  • �ber geeignete Schl�ssel ($i,$j) mehrdimensionale Arrays simulierbar
  • Formatierung von Daten aller Art (auch bin�r, mit Nullbytes) in Strings
  • Implizite Datenwandlung Liste <-> String <-> Zahl bei Bedarf

Ablaufsteuerung in Tcl:

  • z.B. konventionell sequentiell bzw. if, for, while, foreach
  • Bindung von Kommandos an GUI-Ereignisse (Maus, Tastatur)
  • Bindung von Kommandos an Datei-Ereignisse (fileevent)
  • Bindung von Kommandos an Timer-Ereignisse (after)
  • Bindung von Kommandos an Daten-Ereignisse (Variablen-Trace)

Die wichtigsten Kommandos:

  • set varname value ;# Wertzuweisung
  • expr mathexpression ;# Arithmetik und Vergleiche
  • proc name arglist {body} ;# Definition einer Prozedur
  • if {condition} {body}
  • while {condition} {body}
  • foreach iterator liste {body} ;# Iteration �ber Liste
  • for {set i 0} {$i<$max} {incr i} {...} ;# analog zu C
  • return ?value?
  • break ;# aus while/foreach/for-Schleifen
  • error "deutliche Fehlerbeschreibung"

Die wichtigsten Listen-Kommandos:

  • list el... ;# Anlage einer Liste aus Einzelelementen
  • lappend listname el... ;# Anh�ngen weiterer Elemente
  • lindex list index ;# Extraktion eines Elements anhand Position
  • lrange list from to ;# Extraktion eines zusammenh�ngenden Bereichs
  • lsearch list term ;# Suche eines Elements in einer Liste
  • llength list ;# gibt die L�nge der Liste zur�ck
  • split string ?splitchars? ;# Zerlegt einen String an joinchars in Listenelemente
  • join list ?joinchars? ;# F�gt joinchars zwischen je zwei Listenelemente, erzeugt einen String

Die wichtigsten String-Kommandos:

Die wichtigsten I/O-Kommandos:

  • gets channel ?varname? ;# bis Zeilenende (exklusiv)
  • puts channel string ;# mit Zeilenvorschub
  • open filename ?mode? ;# gibt Channel-Id zur�ck
  • close channel
  • read channel ?howmuch? ;# ohne R�cksicht auf Zeilenenden

Tcl hat eine kleine, aber qualifizierte weltweite Anwendergemeinschaft, die vor allem auf folgenden �ffentlichen Wegen kommuniziert:

  • Newsgroup news:comp.lang.tcl ("die freundlichste Gruppe im Usenet")
  • Tcl'ers Wiki, bidirektionale Website mit reichhaltigem Inhalt
  • Tcl'ers Chat (manchmal weltweites Debugging in quasi-Echtzeit)

Beispiel: Berechnung des Mittelwerts einer Zahlenliste

 proc mean L {expr double([join $L +]) / [llength $L]}

Die Elemente der Liste L werden mittels join durch "+"-Zeichen zu einem String verbunden. Dieser wird in expr ausgewertet, durch double in Gleitkommazahl gewandelt (um Integer-Division zu vermeiden) und durch die L�nge der Liste geteilt. Das Ergebnis von expr ist implizit der R�ckgabewert von mean.


Beispiel: Spiegeln einer Liste

 proc lrevert L {
    set i [llength $L]
    set res {}
    while {[incr i -1]>=0} {lappend res [lindex $L $i]}
    set res
 }
 % lrevert {foo bar grill test}
 test grill bar foo

Die Schleife �ber die Elemente der Liste beginnt mit N-1 (N als L�nge der Liste), da String- und Listenindizes ab 0 z�hlen, und endet mit 0. Die entsprechenden Listenelemente werden mit lindex geholt und mit lappend an die Ergebnisliste res geh�ngt. set res ist als letztes Kommando einer proc �quivalent zu return $res.


Beispiel: Introspektion des eigenen Proc-Namens

 proc myProcName {} {lindex [info level -1] 0}

info level gibt Zugriff auf den Aufrufstapel (callstack), info level -1 das Kommando, mit der der Aufrufer dieser Prozedur aufgerufen wurde. Das erste Element des Kommandos (lindex ... 0) ist definitionsgem�� der Name.


Beispiel: Klagloser Incrementor Das Tcl-Kommando incr leistet schnelle Integer-Addition bzw. Subtraktion, vorausgesetzt die Variable existiert bereits. Damit sie im anderen Fall automatisch angelegt wird (wie bei awk �blich), faktorisieren wir die sonst erforderliche Existenzpr�fung an diese proc aus:

 proc inc {varname {amount 1}} {
    upvar 1 $varname var
    if {![info exist var]} {set var 0}
    incr var $amount
 }

Hier wird die lokale Variable var an den in varname �bergebenen Namen einer (m�glichen) Variablen (kann auch Array-Element sein) im Skopus des Aufrufers gebunden. Damit kann var gepr�ft, intialisiert, inkrementiert werden.. und f�r den Aufrufer ist das gleiche mit $varname geschehen.


Beispiel: Zeichenh�ufigkeit

 proc charFreq string {
    foreach char [split $string ""] {
       inc t($char)
    }
    foreach {char count} [array get t] {
       lappend pairs [list $char $count]
    }
    lsort -integer -decreasing -index 1 $pairs
 }

Der string wird in Einzelzeichen gesplittet, �ber die iteriert und ein Element in dem tempor�ren Array t klaglos inkrementiert wird. Der Inhalt des Arrays, eine ungeordnete flache Liste von alternierend Zeichen und Anzahl, wird in eine Liste von Paaren gewandelt, die dann bequem nach absteigender H�ufigkeit sortiert werden kann. String kann Megabytes lang und nat�rlich auch koreanisch, griechisch oder arabisch sein...

 % charFreq Tennessee
 {e 4} {n 2} {s 2} {T 1}

Beispiel: Fehlertolerantes Lesen einer Textdatei

 proc readfile {filename} {
    if ![catch {open $filename} fp] {
        set res [read $fp [file size $filename]]
        close $fp
    } else {set res {}}
    return $res 
 }

Wenn das �ffnen der Datei filename gelingt (der catch nicht anschl�gt), wird sie in ganzer L�nge in die Variable res gelesen und zur�ckgegeben. Andernfalls wird ein leerer String zur�ckgegeben. M�gliche Anwendung als Zeilenz�hler:

 proc wc-l filename {llength [split [readfile $filename] \n] }

Beispiel: Textausgabe in Datei, mit Sicherheitskopie

 proc text2file {text filename} {
    if [file exists $filename] {
        file rename -force $filename $filename.bak
    }
    set    fp [open $filename w]
    puts  $fp $text
    close $fp
 }

Existiert eine Datei namens filename, so wird sie in (filename).bak umbenannt. Dann wird eine neue Datei des Namens zum Schreiben ge�ffnet, der �bergebene String (kann beliebig lang sein) mit abschlie�endem Zeilenvorschub hineingeschrieben, und die Datei geschlossen.


Beispiel: Internet-Dateiherunterlader

 package require http
 http::config -proxyhost proxy -proxyport 80
 puts [http::data [http::geturl [lindex $argv 0]]]

Verwendet wird das mit Tcl mitgelieferte package http. Mit http::config wird der hier �bliche Proxy-Rechner eingestellt. Die bei Aufruf des Scripts angegebene URL wird geladen und ihr Datenanteil (der eigentliche Inhalt) auf stdout ausgegeben, kann jedoch in eine Datei umgeleitet werden:

 $ tclsh ~/tcl/wwwget.tcl www.bahn.de/img/agb.gif > t.gif

Beispiel: Digitaluhr in 6 Zeilen

 proc every {ms body} {
     eval $body
     after $ms [list every $ms $body]
 }
 pack [label .clock -textvar time]
 every 1000 {set ::time [clock format [clock sec] -format %H:%M:%S]}

Zun�chst wird ein einfacher, aber wiederverwendbarer Timer, every, definiert, der den �bergebenen body (Tcl-Kommandos) ausf�hrt und anschlie�end veranla�t, da� er nach ms Millisekunden erneut aufge