Mittwoch, 2. Mai 2012

Einlesen von XML-Dateien (Teil 1)

Das Einlesen von XML-Dateien geht recht einfach von der Hand.

Gegeben ist z. B. folgende XML-Datei:
<libraries>
    <library>
        <path>/media/HD1/Movies/</path>
        <playerpath></playerpath>
        <exclude name="sample,tmp/,temp/,RECYCLE.BIN/"/>
        <description>MyMovies</description>
        <prebuf></prebuf>
        <scrapeLibrary>true</scrapeLibrary>
    </library>
</libraries>

Dann müssen entsprechend dem XML Schema die passenden Go Strukturen definiert werden. Es müssen nur die XML Elemente angeführt werden, welche auch ausgelesen werden sollen.
Das könnte dann z. B. so aussehen:
type LibrariesXml struct {
   Library     []LibraryXml   `xml:"library"`
}
type LibraryXml struct {
   Path        string         `xml:"path"`
   Description string         `xml:"description"`
}

Dann kann man noch eine Go Struktur definieren, in welcher die Anwendung die Daten erwartet.
Das könnte dann z. B. so aussehen:
type Library struct {
   Path        string
   Description string
}
In diesem Trivialbeispiel hat diese zusätzliche Struktur keinen grossen Mehrwert. In komplexeren Fällen kann man aber so die Anwendung besser vom XML Schema entkoppeln.

Der Code zum Einlesen der XML-Datei könnte dann z. B. so aussehen:
func (config Configuration) readLibraries() []Library {
   libs := make([]Library, 0)
   libsFile, err := ioutil.ReadFile(config.LibraryPath)
   if err != nil {
      fmt.Println(err)
      return nil
   }
   libsStruct := LibrariesXml{}
   err = xml.Unmarshal(libsFile, &libsStruct)
   if err != nil {
      fmt.Println(err)
      return nil
   }
   for _, lib := range libsStruct.Library {
      libs = append(libs, Library{lib.Path, lib.Description})
   }
   return libs
}
Die eigentliche Arbeit wird in der Funktion xml.Unmarshal() erledigt: parsen der XML-Datei und umpacken in die Go-Struktur.

Das war's auch schon ...

Samstag, 28. April 2012

So einfach kann Nebenläufigkeit sein

Nebenläufigkeit wird durch Goroutines und Channels realisiert.

Eine Goroutine ist eine Funktion die parallel mit anderen Goroutines im gleichen Adressraum ausgeführt wird. Eine Goroutine wird gestartet, indem dem Funktionsaufruf einfach das Schlüsselwort go vorangestellt wird.
Das geht z. B. so:
go list.Sort()

Ein Channel ist ein Kommunikationskanal, über welchen Goroutines Daten austauschen können.
Das geht z. B. so:
c := make(chan int)
c <- 1
val := <- c

Und zusammen könnte das dann so aussehen:
func generate(c chan int) {
   for i := 0; i < 10; i++ {
      c <= i
   }
   close(c)
}

func main() {
   c := make(chan int)
   go generate(c)
   for i := range c {
      // do something with i
   }
}


Donnerstag, 26. April 2012

Interfaces und ihre Implementierungen


Zwischen Interfaces und deren Implementierungen gibt es keinerlei gegenseitige Abhängigkeiten.

Jeder Typ, welcher die Methoden eines Interfaces implementiert, implementiert damit automatisch das Interface. Das explizite Markieren des Typs mit dem Interface ist nicht notwendig (und auch gar nicht möglich).

Dies eröffnet z. B. die Möglichkeit, für bestehende unabhängig voneinander definierte Typen nachträglich ein gemeinsames Interface zu definieren. Und das ohne Auswirkungen auf die bereits bestehenden Typen.

Mal sehen, wie sich das in der Praxis anwenden lässt.

Return values

Funktionen können mehrere Rückgabewerte haben, und diese können auch mit einem Namen versehen werden.

Dadurch ergeben sich folgende Möglichkeiten:
  • Strikte Trennung von Rückgabewerten und Fehlercodes
    Die Funktion File.Write ist z. B. wie folgt definiert:
    func (file *File) Write (b []byte) (n int, err error)
    Die Anzahl der geschriebenen Bytes steht in n, und der Fehlercode in err.
  • Die Verwendung von Pointern als Aufrufparameter zur Rückgabe von Ergebnissen ist nicht notwendig.
  • Die Definition von ad-hoc-Strukturen zum Gruppieren von mehreren Rückgabewerten ist nicht notwendig
Das Benennen der Rückgabewerte erzeugt verständlicheren Code:

  • Die benannten Rückgabewerte können innerhalb der Funktion wie reguläre Variablen verwendet werden.
  • Durch die Vergabe von selbstsprechenden Namen wird klar, welcher Rückgabewert was beinhaltet.


Dienstag, 24. April 2012

Obacht geben auf den Doppelpunkt

Variablendeklarationen sehen so aus:
var i int
oder
var i = 0

Oder als Kurzform:
i := 0

Wertzuweisungen sehen so aus:
i = 0

Also Obacht geben auf den Doppelpunkt ;-)

Sonntag, 22. April 2012

Composite literals

Das letzte Element eines Composite Literals muss am Ende der Zeile mit ',' oder '}' abgeschlossen werden.

Das ist gut:
   var myMap1 = map[string] int {
                "one": 1,
                "two": 2,
   }

Das ist auch gut:
   var myMap2 = map[string] int {
                "one": 1,
                "two": 2}

Das ist nicht gut:
   var myMap3 = map[string] int {
                "one": 1,
                "two": 2
   }

Sieht am Anfang merkwürdig aus.

Mein Virenscanner mag kein Go

Bei jeder Ausführung eines mit Go erstellten Programms schreit mein Virenscanner auf. Das wird wohl auf die Dauer nervig werden!