Advanced For Expressions in Scala

 
 
Chapter 23
 
For Expressions Revisited
1
 
Overview
 
-
For Expressions
-
N-Queens Problem
-
Querying With For Expressions
-
Translating For Expressions
-
Generalizing The For Notation
2
 
Introduction
 
-
Chapter 16 talks about 
map
, 
flatMap
, and 
filter
-
These functions can make a program difficult to understand
3
case
 
class
 
Person
(name: 
String
,
  
                   isMale: 
Boolean
,
  
                   children: 
Person
*)
 
Suppose we have the following:
4
val
 cece = 
Person
(
“Cece”
,
 
false
)
val
 philip = 
Person
(
“Philip”
,
 
true
)
val
 pam = 
Person
(
“Pam”
,
 
false
, cece, philip)
val
 persons = 
List
(pam, philip, cece)
 
What if we wanted to find the names of all pairs
of mothers and their children in the list?
 
Using 
map
, 
flatMap
, and 
withFilter:
 persons withFilter (p => !p.isMale) flatMap (p =>
 
(p.children map (c => (p.name, c.name))))
result: List[(String, String)] = List((Pam,Cece),
(Pam,Philip))
 
Bit hard to understand, isn’t it?
 
Use a for expression!
for
 (p <- persons; 
if 
!p.isMale; c <- p.children)
yield
 (p.name, c.name)
result: List[(String, String)] = List((Pam,Cece),
(Pam,Philip))
For Expressions
A for expression has the following form:
for
 (
seq
yield
 
expr
for
 (p <- persons; n = p.name; 
if
 (n == “Wanda”))
yield
 n
 
Example:
9
for
 {
 
p <- persons 
   
// generator
 
n = p.name 
   
// definition
 
if 
(n == “Wanda”) 
  
// filter
yield
 n
A 
generator
 has the following form:
pat <- expr
 
-
Every for expression starts with a generator
-
expr will typically return a list
-
pat will get matched against all elements of
the list
11
A 
definition
 has the following form:
pat = expr
 
-
Binds 
pat
 to the value of 
expr
 
-
Most of the time pat will be a variable x
A 
filter
 has the following form:
If 
expr
 
-
expr 
is of type Boolean
13
The N-Queens Problem
 
-
Solution would consist of a list of
coordinates 
(row, column)
 
-
It has to be built up gradually
 
-
Let’s use a recursive algorithm
15
 
-
Assume you have already generated all solutions
of placing k queens on a board of size N x N,
where k is less than N
 
-
To place the next queen in row k + 1, generate
all possible extensions of each previous
solutions by one more queen
16
def
 queens(n: 
Int
): 
List[List[(Int, Int)]] 
= {
 def
 placeQueens(k: 
Int
): 
List[List[(Int, Int)]] 
=
    
if
 (k == 
0
)
        
 List(List())
    
else
        
for
 {
          queens <- placeQueens(k - 
1
)
          column <- 
1
 to n
          queen = (k, column)
          
if
 isSafe(queen, queens)
         } 
yield
 queen :: queens
placeQueens(n)
}
 
-
queens only calls placeQueens
 
-
placeQueens generates all partial
solutions
 
-
placeQueens returns a list of lists
18
queens <- placeQueens(k - 
1
)
-
Iterates through all solutions
19
column <- 
1
 to n
-
Iterates through all possible
columns
queen = (k, column)
-
Defines a new queen to be a
coordinate
if
 isSafe(queen, queens)
-
Checks whether the new queen is
under attack
Querying With For Expressions
 
-
for notation = common operations of
database languages
 
-
Suppose we have the class definition:
case class 
Movie
(title: 
String
, directors: 
String
*)
val
 movies
: List[Movie] 
=
    
List
(
        
Movie
(
            
“Avengers: Infinity War”, “Joe Russo”, “Anthony Russo”
        ),
        
Movie
(
            
“Star Wars: The Empire Strikes Back”, “
Irvin Kershner
        ),
        Movie
(
            “Midsommar”, “Ari Aster”
        ),
        Movie
(
            “Cloverfield”, “Matt Reeves”
        )
    )
for
 (m <- movies; a <- m.directors 
if
 a startsWith 
“Joe"
)
yield
 m.title
result: List[String] = List(Avengers: Infinity War)
 
-
To find the title of all movies whose
director’s name is Joe:
for
 (m <- movies 
if
 (m.title indexOf 
“War"
) >= 
0
)
yield
 m.title
result: List[String] = List(Avengers: Infinity War, Star
Wars: The Empire Strikes Back)
 
-
To find the title of all movies that have the
string “War” in their title:
Translation Of For Expressions
- 
Every for expression can be expressed in
terms of:
 
map
 
flatMap
 
withFilter
 
- Used by the Scala compiler
for expressions with one generator
for
(x <- expr1) 
yield
 expr2
expr1.map(x => expr2)
 
=
for expressions starting with generator
and filter
for
(x <- expr1 
if
 expr2) 
yield
 expr3
for
(x <- expr1 withFilter(x => expr2)
yield
 expr3
expr1 withFilter(x => expr2)
map (x => expr3)
31
for expressions starting with 2 generators
for
(x <- expr1; y <- empr2; seq)
yield
 expr3
expr1.flatMap(x => 
for
(y <- expr2; seq)
yield
 expr3
 
=
32
Translating patterns in generators
Tuples in generators
for
((x
1
, . . . , x
n
)<- expr1) 
yield
 expr2
expr1.map { 
case
(x
1
, . . . , x
n
) =>
 
expr2 }
 
=
Arbitrary patterns in generators
for
(
pat 
<- expr1) 
yield
 expr2
expr1 withFilter {
    case 
pat => 
true
    case 
_ =>
 false
}
 map 
{
   case 
pat => expr2
}
 
=
35
Translating definitions
for
(x
 
<- expr1; y = expr2; seq) 
yield
 expr3
for
((x, y)
 
<- for( x <- expr1) 
yield 
(x,expr2); seq)
yield
 expr3
 
=
36
Translating for loops
- 
Translates in a similar way to for expressions
- Just replace map and flatMap with foreach
for
(x
 
<- expr1) body
expr1 foreach (x => body)
 
=
Going the other way
- You can go from 
map
, 
flatMap
, and 
filter
 to
for expressions
38
object
 
Demo
 {
   
def
 map[A, B](xs: 
List[A]
, f: A => B): 
List[B] 
=
   
for
 (x < xs) 
yield
 f(x)   
// produce a call to map
   
def
 flatMap[A, B](xs: 
List[A]
, f: A => List[B]): 
List[B] 
=
   
for
 (x <-xs; y <- f(x)) 
yield
 y 
// produce a call to flatMap
   
def
 filter[A](xs: 
List[A]
, p: A => 
Boolean
): 
List[A] 
=
   
for
 (x <- xs if p(x)) 
yield
 x 
// produce a call to withFilter
}
Generalizing for
- It is possible for your own data types to
support for expressions
 
- You need to define map, flatMap, withFilter,
and foreach as methods of your data type
41
- Suppose you have a class C representing
some sort of collection
 
- It’s typical to pick the following:
abstract class
 C[A] {
   
def
 map[B](f: A => B): C[B]
   
def
 flatMap[B](f: A => C[B]): C[B]
   
def
 withFilter(p: A => 
Boolean
): C[A]
   
def 
foreach(b: A => 
Unit
): 
Unit
}
Monad
- You can formulate functions map, flatMap,
and withFilter on a monad
 
- You can characterize every monad by map,
flatMap, and withFilter, plus a "unit"
constructor
43
Slide Note
Embed
Share

Delve into the world of for expressions in Scala by revisiting key concepts, including the N-Queens Problem and querying techniques. Learn how to translate for expressions and generalize the notation, building on the foundational knowledge from Chapter 16 on map, flatMap, and filter functions.

  • Scala
  • For Expressions
  • N-Queens
  • Translations
  • Querying

Uploaded on Sep 22, 2024 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. Chapter 23 For Expressions Revisited 1

  2. Overview - For Expressions - N-Queens Problem - Querying With For Expressions - Translating For Expressions - Generalizing The For Notation 2

  3. Introduction - Chapter 16 talks about map, flatMap, and filter - These functions can make a program difficult to understand 3

  4. Suppose we have the following: case class Person(name: String, isMale: Boolean, children: Person*) 4

  5. What if we wanted to find the names of all pairs of mothers and their children in the list? val cece = Person( Cece , false) val philip = Person( Philip , true) val pam = Person( Pam , false, cece, philip) val persons = List(pam, philip, cece)

  6. Using map, flatMap, and withFilter: persons withFilter (p => !p.isMale) flatMap (p => (p.children map (c => (p.name, c.name)))) result: List[(String, String)] = List((Pam,Cece), (Pam,Philip)) Bit hard to understand, isn t it?

  7. Use a for expression! for (p <- persons; if !p.isMale; c <- p.children) yield (p.name, c.name) result: List[(String, String)] = List((Pam,Cece), (Pam,Philip))

  8. For Expressions

  9. A for expression has the following form: for (seq) yield expr Example: for (p <- persons; n = p.name; if (n == Wanda )) yield n 9

  10. for { } yield n p <- persons n = p.name if (n == Wanda ) // generator // definition // filter

  11. A generator has the following form: pat <- expr - Every for expression starts with a generator - expr will typically return a list - pat will get matched against all elements of the list 11

  12. A definition has the following form: pat = expr - Binds pat to the value of expr - Most of the time pat will be a variable x

  13. A filter has the following form: If expr - expr is of type Boolean 13

  14. The N-Queens Problem

  15. - Solution would consist of a list of coordinates (row, column) - It has to be built up gradually - Let s use a recursive algorithm 15

  16. - Assume you have already generated all solutions of placing k queens on a board of size N x N, where k is less than N - To place the next queen in row k + 1, generate all possible extensions of each previous solutions by one more queen 16

  17. def queens(n: Int): List[List[(Int, Int)]] = { def placeQueens(k: Int): List[List[(Int, Int)]] = if (k == 0) List(List()) else for { queens <- placeQueens(k - 1) column <- 1 to n queen = (k, column) if isSafe(queen, queens) } yield queen :: queens placeQueens(n) }

  18. - queens only calls placeQueens - placeQueens generates all partial solutions - placeQueens returns a list of lists 18

  19. queens <- placeQueens(k - 1) - Iterates through all solutions 19

  20. column <- 1 to n - Iterates through all possible columns

  21. queen = (k, column) - Defines a new queen to be a coordinate

  22. if isSafe(queen, queens) - Checks whether the new queen is under attack

  23. Querying With For Expressions

  24. - for notation = common operations of database languages - Suppose we have the class definition: case class Movie(title: String, directors: String*)

  25. val movies: List[Movie] = List( Movie( Avengers: Infinity War , Joe Russo , Anthony Russo ), Movie( Star Wars: The Empire Strikes Back , Irvin Kershner ), Movie( Midsommar , Ari Aster ), Movie( Cloverfield , Matt Reeves ) )

  26. - To find the title of all movies whose director s name is Joe: for (m <- movies; a <- m.directors if a startsWith Joe") yield m.title result: List[String] = List(Avengers: Infinity War)

  27. - To find the title of all movies that have the string War in their title: for (m <- movies if (m.title indexOf War") >= 0) yield m.title result: List[String] = List(Avengers: Infinity War, Star Wars: The Empire Strikes Back)

  28. Translation Of For Expressions

  29. - Every for expression can be expressed in terms of: map flatMap withFilter - Used by the Scala compiler

  30. for expressions with one generator for(x <- expr1) yield expr2 = expr1.map(x => expr2)

  31. for expressions starting with generator and filter for(x <- expr1 if expr2) yield expr3 for(x <- expr1 withFilter(x => expr2) yield expr3 expr1 withFilter(x => expr2) map (x => expr3) 31

  32. for expressions starting with 2 generators for(x <- expr1; y <- empr2; seq) yield expr3 = expr1.flatMap(x => for(y <- expr2; seq) yield expr3 32

  33. Translating patterns in generators

  34. Tuples in generators for((x1, . . . , xn)<- expr1) yield expr2 = expr1.map { case(x1, . . . , xn) => expr2 }

  35. Arbitrary patterns in generators for(pat <- expr1) yield expr2 = expr1 withFilter { case pat => true case _ => false } map { case pat => expr2 } 35

  36. Translating definitions for(x<- expr1; y = expr2; seq) yield expr3 = for((x, y)<- for( x <- expr1) yield (x,expr2); seq) yield expr3 36

  37. Translating for loops - Translates in a similar way to for expressions - Just replace map and flatMap with foreach for(x<- expr1) body = expr1 foreach (x => body)

  38. Going the other way - You can go from map, flatMap, and filter to for expressions 38

  39. object Demo { def map[A, B](xs: List[A], f: A => B): List[B] = for (x < xs) yield f(x) // produce a call to map def flatMap[A, B](xs: List[A], f: A => List[B]): List[B] = for (x <-xs; y <- f(x)) yield y // produce a call to flatMap def filter[A](xs: List[A], p: A => Boolean): List[A] = for (x <- xs if p(x)) yield x // produce a call to withFilter }

  40. Generalizing for

  41. - It is possible for your own data types to support for expressions - You need to define map, flatMap, withFilter, and foreach as methods of your data type 41

  42. - Suppose you have a class C representing some sort of collection - It s typical to pick the following: abstract class C[A] { def map[B](f: A => B): C[B] def flatMap[B](f: A => C[B]): C[B] def withFilter(p: A => Boolean): C[A] def foreach(b: A => Unit): Unit }

  43. Monad - You can formulate functions map, flatMap, and withFilter on a monad - You can characterize every monad by map, flatMap, and withFilter, plus a "unit" constructor 43

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#