Created
February 20, 2020 10:55
-
-
Save kellydavid/eadd339b72ed9a606b93437ac65e8632 to your computer and use it in GitHub Desktop.
fpinscala_chapter04.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.dvdkly.fpinscala | |
import scala.annotation.tailrec | |
object Chapter04 { | |
/** | |
* Exercise 4.2 | |
*/ | |
object option { | |
sealed trait Option[+A] { | |
// apply f if the Option is not None | |
def map[B](f: A => B): Option[B] = | |
this match { | |
case Some(a) => Some(f(a)) | |
case None => None | |
} | |
// apply f, which may fail, to the Option if not None | |
def flatMap[B](f: A => Option[B]): Option[B] = this.map(f).getOrElse(None) | |
// B >: A ie. B must be a supertype of A | |
def getOrElse[B >: A](default: => B): B = | |
this match { | |
case Some(a) => a | |
case None => default | |
} | |
// don't evaluate ob unless needed | |
//returns the first Option if it’s defined; otherwise, it returns the second Option. | |
def orElse[B >: A](ob: => Option[B]): Option[B] = | |
// val x: Option[Option[A]] = map(a => Some(a)) | |
map(Some(_)) getOrElse ob | |
//convert Some to None, if the value doesn't satisfy f | |
def filter(f: A => Boolean): Option[A] = this.flatMap(a => if(f(a)) Some(a) else None) | |
} | |
case class Some[+A](get: A) extends Option[A] | |
case object None extends Option[Nothing] | |
object Option { | |
/** | |
* The map function lets us operate on values of type Option[A] using a function of type A => B, | |
* returning Option[B]. Another way of looking at this is that map turns a function f of type A => B into a | |
* function of type Option[A] => Option[B] . | |
*/ | |
def lift[A,B](f: A => B): Option[A] => Option[B] = _ map f | |
val absO: Option[Double] => Option[Double] = lift(math.abs) | |
/** | |
* Exercise 4.3 | |
* | |
* Write a generic function map2 that combines two Option values using a binary func- tion. If either Option | |
* value is None, then the return value is too. | |
*/ | |
def map2[A, B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = a.flatMap(a => b.map(f(a, _))) | |
/** | |
* Exercise 4.4 | |
* Write a function sequence that combines a list of Options into one Option containing a list of all the Some | |
* values in the original list. If the original list contains None even once, the result of the function should | |
* be None; otherwise the result should be Some with a list of all the values. Here is its signature:3 | |
*/ | |
def sequence[A](a: List[Option[A]]): Option[List[A]] = | |
a.foldRight[Option[List[A]]](Some(Nil))((opA, opB) => map2(opA, opB)(_ :: _)) | |
def parseInts(a: List[String]): Option[List[Int]] = sequence(a map (i => `try`.Try(i.toInt))) | |
/** | |
* Exercise 4.5 | |
* Implement this function. It’s straightforward to do using map and sequence, but try for a more efficient | |
* implementation that only looks at the list once. In fact, imple- ment sequence in terms of traverse. | |
*/ | |
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = | |
a.foldRight[Option[List[B]]](Some(Nil))((a, acc) => map2(f(a), acc)(_ :: _)) | |
def sequence2[A](a: List[Option[A]]): Option[List[A]] = | |
traverse(a)(x => x.) | |
// { | |
// @tailrec | |
// def loop(input: List[A], acc: List[B]): Option[List[B]] = { | |
// input match { | |
// case Nil => Some(acc) | |
// case x :: xs => f(x) match { | |
// case Some(b) => loop(xs, acc ++ List(b)) | |
// case None => None | |
// } | |
// } | |
// } | |
// loop(a, List.empty) | |
// } | |
} | |
} | |
object testOption{ | |
import option._ | |
val x: Option[Int] = Some(33) | |
val y: Option[Int] = x.orElse(Some(42)) | |
} | |
object employee { | |
import option._ | |
case class Employee(name: String, department: String) | |
def lookupByName(name: String): Option[Employee] = ??? | |
val joeDepartment: Option[String] = lookupByName("Joe").map(_.department) | |
} | |
object variance { | |
import option._ | |
def mean(xs: Seq[Double]): Option[Double] = | |
if(xs.isEmpty) None | |
else Some(xs.sum / xs.length) | |
// math.pow(x - m, 2) where m is the mean | |
def variance(xs: Seq[Double]): Option[Double] = | |
mean(xs) | |
.map( m => xs.map(x => math.pow(x - m, 2))) | |
.flatMap(mean) | |
} | |
object `try` { | |
import option._ | |
def Try[A](a: => A): Option[A] = | |
try Some(a) | |
catch { case e: Exception => None } | |
} | |
object insurance { | |
import option._ | |
import `try`._ | |
/** | |
* Top secret formula for computing an annual car | |
* insurance premium from two key factors. | |
*/ | |
def insuranceRateQuote(age: Int, numberOfSpeedingTickets: Int): Double = ??? | |
def parseInsuranceRateQuote( age: String, numberOfSpeedingTickets: String): Option[Double] = { | |
val optAge: Option[Int] = Try(age.toInt) | |
val optTickets: Option[Int] = Try(numberOfSpeedingTickets.toInt) | |
Option.map2(optAge, optTickets)(insuranceRateQuote) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment