Use distanceComparator to create a comparator function when needed
Can create different comparators with different reference points
Global state would only allow one comparator at a time
1 2
val referencePoint = newPhysicsVector(0.5, 0.5, 0.0) val sortedPoints = NergeSort.mergeSort(points, distanceComparator(referencePoint))
Collection Methods
We can apply first-order functions to compress our code when working with data structures
We’ll see a variety of methods that take functions as parameters to help us work with data
For Each
Call a function on each elemnents of a List
Only use for the side-effects
ie. Not too useful when embracing immutability
1 2 3 4 5 6 7 8 9
val words: List[String] = List("zero", "one", "two", "three") words.foreach(println) /*** Will print: zero one two three ***/
Filter
Takes a function that returns a Boolean
Returns a new List containing only the elements for which the function returns true
1 2 3 4 5 6 7 8
val words: List[String] = List("zero", "one", "two", "three") val filteredWords: List[String] = words.filter(_.length > 3) filteredWords.foreach(printtln) /*** Will print: zero three ***/
Map
Takes a function of the data type to another data type
Returns a new List containing the retun values of the function with each element as an input
1 2 3 4 5 6 7 8 9 10 11
val numbers: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0) val numberSquared: List[Double] = numbers.map(Math.pow(_, 2.0)) numbersSquared.foreach(println) /*** Will print: 1.0 4.0 9.0 16.0 25.0 ***/
The map method takes 2 type parameters
We can provide a function that “maps” the elements to a different type
The types can be inferred by the types of the provided function
1 2 3 4 5 6 7 8 9 10
val words: List[String] = List("zero", "one", "two", "three") val wordLengths: List[Int] = words.map(_.length) wordLengths.foreach(println) /*** Will print: 4 3 3 5 ***/
Yield
As alternate syntax to map, we can use the yield keyword
Add the keyword yield before the body of a loop
The last expression of the loop body will be “collected” at each iteration
1 2 3 4 5 6 7 8 9 10 11 12 13
val numbers: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0) val numbersSquared: List[Double] = for (number <- numbers) yield { Math.pow(number, 2.0) } numbersSquared.foreach(println) /*** Will print: 1.0 4.0 9.0 16.0 25.0 ***/
Using yield will create a data structure of the same type as the one being iterated over
It’s not always possible to match the type exactly
Scala will default to certain data structure
Use toList tp convert the default type to a List
1 2 3 4 5 6 7 8 9 10 11 12
val numberSquared: List[Double] = (for(number <- 1 to 5) yield { Math.pow(number, 2.0) }).toList numbersSquared.foreach(println) /*** Will print: 1.0 4.0 9.0 16.0 25.0 ***/
Reduce
Takes a function that combines two values of the data type intyto a single value of that type
Calls this function on all elements
Combines the data into a single value
The first parameter of the function is the accumulator
Stores the total value accumulated so far
Initialized as the first element (Note: This example breaks if 1.0 is not the first elementa)
1 2 3 4
val numbers: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0) val sumSquares: (Double, Double) => Double = (a: Double, b: Double) => a + Math.pow)(b, 2.0) val sumOfSquares: Double = numbers.reduce(sumSquares) println(sumOfSquares) // 55.0
We can use the _ shorthand with two parameters
The order of appearance of the _’s is the parameter order
Can not use _ shorthand if you need to use a input twice
1 2 3
val numbers: List[Double] = List(1.0, 2.0, 3.0, 4.0, 5.0) val sumOfSquares: Double = numbers.reduce(_ + Math.pow(_, 2.0)) println(sumOfSquares) // 55.0
Fold
Similar to reduce
Use fold if you need to initialize your accumulator
Use fold if you are reducing a different type than the data type
To accumulate to a type different than the data type
Use the left / right version of fold
Initial value determines the accumulator type
This value is returned if the input is the empty list
1 2 3 4 5
val words: List[String] = List("zero", "one", "two", "three") val totalLength: Int = words.foldLeft(0)(_ + _.length) val totalLength2: Int = words.foldRight(0)(_.length + _) println(totalLength) // 15 println(totalLength2) // 15
Using fold defaults to foldLeft
Start with the first (left=most) element
To accumulate from the end of the List use foldRight
Must reverse the parameter order when using foldRight / reduceRight
Accumulator is second parameter, data is first element
Example - Polunomials
———–Check the Lecture Recording—————
Lecture Question
Restriction: No state is allowed in this question. Specifically, the keyword “var” is banned. (ie. You are expected to use a recursive solution)
Question: In a package named “functions” add to the object named Numbers (The object with your fib method) a method named averageInRange that:
Takes a List of Doubles as a parameter
Returns a functions that takes 2 Doubles and returns a Double
This function will return the average of all the numbers in the List that are between the two input Double
Exclude the endpoints (ie. Use < and >, not <= and >=)
The first parameter of the function is the min value and the second is the max value of the range
Ex. Averaging the list (1.0, 4.0, 2.0, 5.0, 3.0) with endpoints 1.5 and 4.9
Average in range is the average of (2.0, 3.0, 4.0) == 3.0
Testing: In a package named “tests” create a class named “TestAverageInRange” as a test suite that tests all the functionality listed above. (You don’t have to test end point exclusion since Doubles are not reliably equal)