07 Anfragen für Listen

Werbung
Anfragen für Listen
Kapitel 7 des Buches, von Java-Selbstbau nach Scala-Library
portiert.
2014-11-14 Christoph Knabe
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
1
MapReduce-Verfahren
Google u.a. verwenden Map-Reduce-Verfahren zur
Verarbeitung riesiger Datenmengen
”Our abstraction is inspired by the map and reduce
primitives present in Lisp and many other functional
languages.”
Jeffrey Dean und Sanjay Ghemawat, Google Labs
Map kommt gleich
Reduce entspricht im Wesentlichen der Faltung
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
2
Select-Statements
Am Ende des Kapitels können wir Listen mit selectähnlicher Funktionalität bearbeiten.
Im Kleinen geht das jetzt schon:
SQL:
Java:
Übung
IntList
select sum(v)
from values
values.sum()
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
3
Vorbereitungen
Wir werden wieder mit Funktionen höherer Ordnung
arbeiten:
type Predicate[T] = T => Boolean
Beispiel:
def even: Predicate[Int] = _ % 2 == 0
Wir benutzen jetzt aber die generische ScalaCollections-Bibliothek.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
4
Primitive Listenoperationen
Erzeugen einer leeren Liste:
Nil
List[Int]()
List.empty
List.empty[Int]
//Typ
//Typ
//Typ
//Typ
List[Nothing]
List[Int]
List[Nothing]
List[Int]
Prepend-Operation heißt :: (rechtsassoziativ)
val list12 = 1 :: 2 :: Nil
Multi-Arg-Fabrikmethode im Begleitobjekt:
val list123 = List(1, 2, 3)
Zugriffsmethoden:
list123.head == 1
&& list123.tail == List(2, 3)
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
5
Listenelemente filtern
Nachbau in Klasse ListTest:
def filter[T](
list: List[T], predicate: Predicate[T]
): List[T] = {
if (list isEmpty) return list
val filteredTail = filter(list.tail, predicate)
if(predicate(list.head)){
list.head :: filteredTail
}
else filteredTail
}
Ist aber schon in scala.List enthalten.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
6
Ein Beispiel
Eine Liste aller geraden Zahlen zwischen 1 und 10
erzeugen:
(1 to 10).toList
filter even
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
7
Abbildungen
Entsprechend der where -Bedingung des folgenden
select-Statements können wir bereits filtern.
select v*v
from values
where v mod 2 = 0
Die Umsetzung des select-Teils für Listen erfordert,
dass wir Listenelemente auch abbilden können.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
8
Abbildungen
Nehmen wir eine Int-Funktion:
Int => Int
Beispiel:
def square: Int=>Int = x=>x*x
Wir definieren damit eine Funktion höherer Ordnung:
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
9
Die Funktion map
Bildet jedes Element einer Liste mit der Funktion f ab:
//In Klasse ListTest:
def map[T,R](
list: List[T], f: T => R
): List[R] = {
if (list isEmpty) return Nil
f(list head) :: map(list.tail, f)
}
map ist vordefiniert in Klasse List.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
10
Beispiel
Eine Liste der Quadrate der geraden unter den ersten
10 Zahlen ermitteln:
(1 to 10).toList filter even
map square
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
11
Der Werkzeugkasten
Die Methoden map und filter bilden zusammen mit
der Methode zip (später) einen Werkzeugkasten mit
dem wir interessante Beispiele finden können.
Übrigens:
map und filter können mit Hilfe der Faltung
implementiert werden.
Versuchen Sie es mal!
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
12
Primzahlen (Sieb des Eratosthenes)
def primes(list: List[Int]): List[Int] = {
if (list isEmpty){return list}
val head = list.head
val filteredTail = list.tail.filter(
_ % head != 0
)
head :: primes(filteredTail)
}
def primes(high: Int): List[Int] = {
val numbers: List[Int] = (2 to high) toList;
primes(numbers)
}
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
13
Listen verknüpfen: Konkatenation
def concat(
left: List[Int], right: List[Int]
): List[Int] = left match {
case Nil => right
case x :: tail => x :: concat(tail, right)
}
Die Konkatenation ist vordefiniert als Operator :::
val left = List(1, 2, 3, 4)
val right = List(5, 6, 7, 8, 9)
Left
::: right //1 2 3 4 5 6 7 8 9
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
14
Listen verknüpfen: zip
Mischen zweier Listen im „Reißverschlussverfahren“:
def zip(
left: List[Int], right: List[Int]
): List[(Int,Int)] = (left,right) match {
case (x :: xs, y :: ys) =>
(x, y) :: zip(xs, ys)
case _ => Nil
}
Das Mischen ist vordefiniert als Methode List.zip
val left = List(1, 2, 3, 4)
val right = List(5, 6, 7, 8, 9)
left
zip right //(1,5), (2,6), (3,7), (4,8)
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
15
Der Abstand zweier Punkte
Ein Punkt sei durch eine Liste seiner Koordinaten dargestellt.
def distance(
pointA: List[Double], pointB: List[Double]
) = {
val paired = pointA zip pointB
val squaresOfDiffs
= paired.map(p => p._1 - p._2).map(x => x*x)
val sum = squaresOfDiffs.foldLeft(0.0)(_ + _)
Math.sqrt(sum)
}
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
16
Listen aufteilen: partition
partition filtert positive und negative Treffer:
val numbers = List(
8, 4, 7, 9, 2, 1, 5, 3, 6
)
val result = numbers.partition(_ < 5)
Es wird ein Paar geliefert mit
1. allen Erfüllern des Prädikats
2. allen Nichterfüllern:
(List(4, 2, 1, 3), List(8, 7, 9, 5, 6))
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
17
Quicksort-Beispiel
Das erste Element wird jeweils als Pivot p verwendet.
sort(8 4 7 9 2 1 5 3 6) mit p=8 →
sort(4 7 2 1 5 3 6) 8 sort(9) mit p=4 →
sort(2 1 3) 4 sort(7 5 6) 8 9 mit p=2,7→
sort(1) 2 sort(3) 4 sort(5 6) 7 sort() 8 9 →
1 2 3 4 sort() 5 sort(6) 7 8 9 →
123456789
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
18
Quicksort
def sort(xs: List[Int]): List[Int] = xs match {
case Nil
=> Nil
case pivot :: tail =>
val (left,right) = tail partition (_ <= pivot)
sort(left) ::: pivot :: sort(right)
}
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
19
Alles klar?
Mit Hilfe einiger Funktionen können Listen deklarativ
verarbeitet werden. Insgesamt bieten die Funktionen
einen Komfort, der ähnlich wie bei SQL ist.
 filter: Ein Prädikat wird als Parameter
übergeben. Es wird eine Liste konstruiert, deren
Elemente alle dem Prädikat genügen
 partition: wie filter, aber es werden positive
und negative Treffer als getrennte Listen geliefert.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
20
Alles klar?
 map: Eine Abbildung wird als Parameter
übergeben. Die Abbildung wird für alle
Listenelemente durchgeführt.
 zip: Mischt zwei Listen nach dem
Reißverschlussprinzip zu einer Liste von Paaren.
L. Piepmeyer: Funktionale Programmierung - Abfragen für Listen
21
Herunterladen