Vorlesung10 - Informatik

Werbung
Programmieren
in
Scala
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
232 / 300
Motivation
• Aus Sicht unseres Sotwaretechnik-Lehrstuhls:
Scala ist eine der derzeit modernsten Programmiersprachen
• KIV wird derzeit nach Scala portiert (fast fertig)
• KIV ist dann mit Scala und Java (GUI) programmiert.
• Scala unterstützt Konzepte die vieles mit den Konzepten der
KIV-Spezifikationen gemeinsam haben.
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
233 / 300
Was ist Scala?
• Scala ist entwickelt an der Uni Lausanne von Prof. Odersky
http://www.scala-lang.org
• Viele Dokus (Tutotial, etc.) kann man dort finden
• Eclipse-IDE: //http://www.scala-ide.org
• Scala ist eine objektorientierte Sprache
• Alle Konzepte von Java (Methoden, Klassen, Vererbung etc.) gibt es
(z.T. in verbesserter Form) auch in Scala
• Scala wird in ByteCode der JVM compiliert
• Scala unterstützt auch die Konzepte aus funktionalen Sprachen
(Higher-Order Funktionen Pattern Matching etc.)
• Die Syntax von Scala ist etwas anders, und deutlich verbessert
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
234 / 300
Scala: Typen
• Scala kennt wie Java Klassen und generische Klassen
• Oberster Typ ist Any statt Object
• Scala kennt keine primitiven Typen:
• statt int und Integer gibt es nur Int
• analog: bool, Boolean ⇒ Boolean, array, Array ⇒ Array
• generische Typen werden mit eckigen Klammern geschrieben:
Array[Int] statt Array<Int>
• Array-Zugriff mit runden Klammern a(i) (statt a[i])
• Fest vordefiniert sind Tupel mit (für 3-Tupel) Typ (A,B,C). Sie
werden auch als (a,b,c) konstruiert. Die Felder werden z.B. mit
(a,b,c). 2 selektiert (liefert b)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
235 / 300
Scala: Methodendefinition
• Scala kennt wie Java statische und dynamische Methoden
• Java:
type method(argty1 arg1, argty2 arg2, ...){
body
}
• Scala:
def method(arg1:argty1,arg2:argty, ...):type = {
body
}
• Der Typ void heisst Unit in Scala
• Methoden ohne Resultat können vereinfacht als
def method(arg1:argty1, ... ) { body }
geschrieben werden (kein Typ und kein Gleichheitszeichen)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
236 / 300
Scala: Methodenaufruf
• Aufruf wie in Java für statische und dynamische Methoden
type . smethod (arg1, arg2, . . . )
object . dmethod (arg1, arg2, . . . )
• Bei Methoden ohne Argumente dürfen Leerklammern weggelassen
werden (auch schon bei der Definition)
• Konvention: Leerklammern weglassen gdw. keine Seiteneffekte:
z.B. Selektoren (“getter”) und Tests: list.length, list.isEmpty
• Vorteil: Feldzugriff kann lokal auf get-Methode (gleichen Namens)
geändert werden (keine Änderung in anderen Klassen!)
• Dynamischen Methoden mit einem Argument darf man mit
object method arg aufrufen (Infix: weder Punkt noch Klammern!)
• Vorteil: +, * etc. haben keine Sonderrolle mehr
(sie können auch überladen werden).
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
237 / 300
Ausprobieren von Scala
• Scala kann man wie Java compilieren und ein Programm
main(arglist:Array[String]):Unit in einem object ausführen.
• Scala kann aber auch mit einem Kommandozeileninterpreter
(entweder von innerhalb Eclipse oder standalone) bedienen
• Aufruf von scala gibt scala>-prompt
• Eintippen von Ausdrücken wertet diese aus
scala>
"Hello
scala>
7
scala>
2
scala>
6
"Hello" + " World!"
World!"
3 + 4
new Array(4,5).length
new Array(5,6)(1)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
238 / 300
Scala: Felder und lokale Variablen
• Scala unterscheidet bei Feldern und Variablen überschreibbare (var)
und nicht überschreibbare (val; Java: final)
• nicht überschreibare Felder/Variablen val x:Int = 5
• überschreibare Felder/Variablen var x:Int = 5
• Scala implementiert eine Typinferenz:
Für die meisten Variablen und initialisierten Felder kann die
Typangabe wegfallen.
• Für das obige Beispiel: val x = 5 ist ok
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
239 / 300
Scala: Methodenrümpfe
• Scala kennt keinen Unterschied zwischen Statement und Expression.
• Statements sind einfach Expressions vom Typ Unit
• Deshalb ist mischen erlaubt, z.B.:
val x = if (y > 5) { val y = 3; y + 2} else 5
• Der ?-Operator von Java ist in Scala überflüssig
• In Scala werden Strichpunkte nur benötigt, wenn zwei Statements
auf derselben Zeile stehen (sehr selten)
• Zeilenumbruch fügt (wo sinnvoll) implizit einen Strichpunkt ein
• Wenn return expr das letzte Statement einer Methode ist, darf nur
expr geschrieben werden.
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
240 / 300
Scala: Beispele für Methoden
• Fakultätsfunktion:
def fac(x:Int):Int = {if (x == 0) 0 else fac(x - 1)}
• Länge (wie in Listenklasse vordefiniert):
def length:Int = {if (isEmpty) 0 else tail.length + 1}
• Letztere Funktion würde in Java so aussehen:
int length() {if (isEmpty) return 0;
else return tail.length() + 1;}
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
241 / 300
Scala: Klassendefinitionen (1)
• Java Klassen enthalten sowohl statische als auch dynamische
Methoden
• Scala teilt die Methoden auf in dynamische in der Klasse und
statische, die im sog. companion object gleichen Namens stehen
• Beide müssen in dieselbe Datei, eines von beiden darf fehlen
• Wenn nur object ⇒ Singleton.
object A {
def staticmethod(arg:Int):Int = { 2 * arg }
}
class A {
val field = 5
def dynamicmethod(arg:Int):Int = { this.field + arg}
}
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
242 / 300
Scala: Klassendefinitionen (2)
• Java Klassen haben immer einen (häufig nutzlosen) nullstelligen
Konstruktor
• meist muss einer definiert werden, der einfach nur Felder initialisiert.
• In Scala stattdessen: Felder, die im Konstruktor initialisiert werden
sollen, als Argumente der Klasse. Kein nullstelliger Konstruktor.
class A(val field1:Int, var field2:Int) {...}
ergibt als möglichen Konstruktoraufruf new A(3,5). Ein nullstelliger
Konstruktoraufruf ist nicht möglich.
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
243 / 300
Scala: Abstrakte Datentypen (1)
Scala unterstützt freie Datentypen analog zu KIV-Specs.
Beispiel: arithmetische Ausdrücke.
sealed abstract classed AExp
case class Binop(val e1:AExp, val str:String, val e2:AExp) extends AExp
case class Unop(val str:String, val e:AExp) extends AExp
case class Val(val v:Int) extends AExp
case class Var(val id:String) extends AExp
erlaubt zu schreiben:
Binop(Var("x"),"+",Unop("-",Val(4))))
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
244 / 300
Scala: Abstrakte Datentypen (2)
• Ein abstrakter Datentyp besteht aus einer abstrakten
(Summen-)Klasse (AExp) und einer Anzahl Unterklassen
(Summanden; hier Binop, Unop, Val, Id)
• Alle Klassen werden zusammen in die Datei AExp
der Summenklasse geschrieben
• sealed ⇒ keine (weiteren) Unterklassen in anderen Dateien
• case class: erlaubt Konstruktoraufruf ohne Schlüsselwort new
(und Pattern matching; siehe später)
• Die Felder sind meist nicht schreibbar (val),
sie werden nur vom Konstruktor initialisiert
(“immutable” datatype; entspricht algebraischen Datentypen).
• Gleichheit (i.e. ==) vergleicht case classes strukturell (nicht wie in
Java auf Referenz-Gleichheit).
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
245 / 300
Scala: Listen (1)
Listen sind ein vordefinierter freier Datentyp
sealed abstract class List[+A]
case class ::[A](val head:A, tail:List[A]) extends List[A]
case object Nil extends List[Nothing]
erlaubt Konstruktion mit 1::2::Nil
Bem: eigentlich ::(1,::(2::Nil)), aber “::” ist auch eine Infixfunktion auf Listen, die mit
Doppelpunkt endet. Solche Infixfunktionen drehen die Argumentreihenfolge um!
Alternativ: List(1,2) (durch Aufruf der statischen Methode List mit
variabler Argumentzahl im companion object der Klasse List)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
246 / 300
Scala: Listen (2)
• Listen erlauben kein Überschreiben des head oder tail.
• Listen sind kovariant (das bedeuet das + vor dem Typparameter):
jedes x:List[Int] ist auch ein x:List[Any]
jedes x:List[Binop] ist auch ein x:List[AExp]
• allgemein x:List[type1] ist Subtyp von x:List[type2] falls type1 ein
Subtyp (Unterklasse) von type2 ist.
• Arrays sind dagegen nicht kovariant (weil modifizierbar)
• Nothing ist der leere Typ ohne jedes Element (Subtyp von jedem
Typ). Nil ist deshalb vom Typ List[type] für jeden Typ type
• Listen haben viele vordefinierte Funktionen, u.a. ++ (append), x(i)
(get für das i-te Element, 0-basiert); siehe scaladoc
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
247 / 300
Scala: Pattern Matching
• Scala erlaubt Pattern Matching für ADTs
• match verallgemeinert Java’s switch.
• Methode allids (in Klasse AExp) sammelt
alle vorkommenden Variablen des Ausdrucks
• Funktion ++ hängt Listen aneinander.
• Ein Underscore steht für wildcard (beliebig)
def allids:List[String] = {
this match {
case Var(id) => List(id)
case Binop(e1, ,e2) => e1.allids ++ e2.allids
case Unop( ,e0) => allids(e0)
case Val( ) => Nil
}
}
Binop(Var(”x”),”+”,Val(4)).allids ergibt List(”x”)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
248 / 300
Scala: Higher-Order Funktionen
• Scala erlaubt Higher-Order Functions,
i.e. Funktionen, die Funktionen als Argumente bekommen.
• Funktionstypen werden A => B geschrieben.
• Beispiel: Funktion map (in Klasse List[A] definiert)
wendet eine Funktion f auf alle Listenelemente an
def map[B](f:A => B):List[B] = {
this match { Nil => Nil
x :: xs => f(x) :: xs.map(f)
}}
oder alternativ:
def map[B](f:A => B):List[B] = {
if (isEmpty) Nil else f(head) :: tail.map(f)
}
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
249 / 300
Scala: lambda-Abstraktionen
• Das Funktionsargument von map kann eine Funktion sein, die mit
def definiert wurde
• Da die Funktion meist nur einmal verwendet wird, gibt es auch die
Möglichkeit, sie als lambda-Abstraktion anzugeben
• x:Int => x + 1 ist die Funktion, die eins addiert
• (x:Int,y:Int) => x + y ist die Additionsfunktion
• Verwendung z.B mit map:
List(1,2,3).map((x:Int) => 2 * x) ergibt List(2,4,6)
• Kurzschreibweise: e(_) statt (x:Int => e(x))
(manchmal ist Typangabe notwendig: e(_:Int)
• Beispiel: List((1,2),(4,5)).map(_._1) == List(1,4)
(mit Selektor . 1 für Paare)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
250 / 300
Scala: Datentyp Map
• Eine Map bildet endlich viele Schlüssel (keys) auf Werte (values) ab.
• Analog zu Liste von Paaren, aber keine doppelten Schlüssel
• Paare kann man sowohl mit (a,b) als auch mit a -> b konstruieren.
• Konstruktion aus Paaren mit Map("x" -> 3, "y" -> 4)
• Das letzte zählt: Map("x" -> 3, "x" -> 4) == Map("x" -> 4)
• Zugriff über Map("x" -> 3, "y" -> 4)("x") ergibt 3
• Addieren: Map("x" -> 3) + ("y" -> 4) ergibt
Map("x" -> 3, "y" -> 4)("x")
• Löschen mit Map("x" -> 3, "y" -> 4) - "x" gibt
Map("y" -> 4)
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
251 / 300
Scala: viele weitere Features
Scala hat noch viele andere Features, auf die wir hier nicht eingehen:
• lazy Funktionen und Felder
• interfaces erweitert scala zu traits.
Diese dürfen auch Code definieren.
• implizite Argumente
• selbstdefiniertes Pattern Matching
• erweiterte generische Syntax für Schleifen
• ...
17. Juni 2013
G. Schellhorn, D. Haneberg: Formale Methoden im Software Engineering
252 / 300
Herunterladen