Teil 2: Funktionale Programmierung

Game of Life mit Scala – das Tutorial

Heiko Seeberger

Swing-Oberfläche

Nun sind wir fast am Ziel angelangt. Aber natürlich brauchen wir noch eine grafische Oberfläche, denn es ist unglaublich faszinierend, der Entwicklung von Generationen zuzuschauen. Da es hier um ein Tutorial geht und nicht um eine mächtige Visualisierung für das Game of Life, beschränken wir uns auf eine einfache Oberfläche, dargestellt in Abbildung 2, mit der folgenden Funktionalität:

  • Wir stellen nur einen begrenzten Ausschnitt des unendlichen Spielfeldes dar
  • Mit der Maus können wir jederzeit den Status der dargestellten Zelle ändern
  • Mit einem Button gelangen wir zur nächsten Generation

Abb. 2: Swing-Oberfläche

Wir wollen alleine mit Scala-Bordmitteln auskommen. Daher verwenden wir die Scala-Swing-Bibliothek [4], die die Arbeit mit Swing deutlich vereinfacht. Leider können wir hier nicht mehr im Detail auf die Implementierung eingehen, die in Listing 4 gezeigt ist. Das würde trotz der Knappheit des Codes den Rahmen dieses Tutorials sprengen, insbesondere weil einige Konzepte angewendet werden, die weit über das Einstiegsniveau hinausgehen. Vielmehr wollen wir im Folgenden ein paar Highlights herauspicken.

Zunächst sehen wir, wie einfach wir eine Swing-Anwendung erstellen können: Wir erweitern einfach SimpleSwingApplication und überschreiben die top-Methode. Komponenten bauen wir auf, indem wir zum Feld contents weitere Komponenten hinzufügen. Events behandeln wir, indem wir für Komponenten das Feld reactions überschreiben und so genannte Reactions hinterlegen. Actions können wir direkt mit einem Button verknüpfen, indem wir das Singleton Object Button „aufrufen“ und den Code für die Action als Argument übergeben. Dann begegnen uns einige Konzepte wieder, die wir schon in den vorigen Schritten angewendet haben, z. B. eine For Comprehension, um das begrenzte Spielfeld aufzubauen, oder eine Funktion, die als Parameter übergeben wird, um zu bestimmen, ob eine Zelle lebendig oder tot ist.

Auch wenn die Klasse SwingUI insgesamt über das hinausgeht, was wir bisher gelernt haben, so können wir doch einen Eindruck davon gewinnen, was mit Scala im Allgemeinen und mit der Scala-Swing-Bibliothek im Speziellen möglich ist.

Fazit

Wir haben in diesem Tutorial viele wichtige Scala-Grundlagen kennengelernt, sowohl im Hinblick auf die Sprache selbst, als auch betreffend Werkzeugen wie die REPL und SBT. Hoffentlich konnten wir durch das praktische Beispiel vermitteln, dass Scala nicht nur knappen (z. B. Cell) und gut verständlichen (z. B. CellSpec) Code möglich macht, sondern durch den funktionalen Charakter oft auch sehr einfache Lösungen (z. B. Generation.aliveNeighbours) erlaubt. Selbstverständlich freuen wir uns über Rückmeldungen, sei es in Form von Fragen, Bug-Reports (bitte über den Issue Tracker [5]) oder Ideen, wie wir das Tutorial verbessern oder ausbauen können. Viel Spaß mit Scala!

Listing 4: SwingUI.scala
package com.weiglewilczek.gameoflife
import java.awt.Color
import javax.swing.border.LineBorder
import scala.swing._
import scala.swing.event.MouseClicked
object SwingUI extends SimpleSwingApplication {
  override def startup(args: Array[String]) {
    dimension = args.headOption map { arg =>
      try { arg.toInt } catch { case _ => DefaultDimension }
    } getOrElse DefaultDimension
    super.startup(args) 
  }
  override def top = new MainFrame {
    title = "Game of Life"
    resizable = false
    contents = new BoxPanel(Orientation.Vertical) {
      contents += new GenerationPanel(new Generation, update(contents(0) = _))
      contents += Button("Next") {
        val next = contents(0).asInstanceOf[GenerationPanel].generation.next
        update(contents(0) = _)(next)
      }
    }
    private def update(swap: GenerationPanel => Unit)(generation: Generation) {
      swap(new GenerationPanel(generation, update(swap)))
      pack()
    }
  }
  private class GenerationPanel(
      val generation: Generation,
      update: Generation => Unit)
    extends GridPanel(dimension, dimension) {
    contents appendAll cells
    private lazy val cells = {
      def cell(cell: Cell, alive: Cell => Boolean) = {
        new FlowPanel {
          border = LineBorder.createBlackLineBorder
          background = if (alive(cell)) Color.BLUE else Color.WHITE
          contents += new Label("")
          listenTo(mouse.clicks)
          reactions += {
            case e: MouseClicked => {
              val aliveCells =
                if (alive(cell)) generation.aliveCells - cell
                else generation.aliveCells + cell
              update(new Generation(aliveCells))
            }
          }
        }
      }
      for {
        y 
Heiko Seeberger ist geschäftsführender Gesellschafter der Weigle Wilczek GmbH und verantwortlich für die technologische Strategie des Unternehmens mit den Schwerpunkten Java, Scala, OSGi, Eclipse RCP, Lift und Akka. Zudem ist er aktiver Open Source Committer, Autor zahlreicher Fachartikel und Redner auf einschlägigen Konferenzen.
Geschrieben von
Heiko Seeberger
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.