Lecture 6. List applications

/*
Topic 1. Liste (din Scala).
 */
 
val l = List(1,2,3)
//Cons(1,Cons(2,Cons(3,Void)))
 
1 :: 2 :: 3 :: Nil
 
List(1,2,3) match {
  case Nil => 0
  case x :: y :: Nil => 2
  case x :: y :: _ => 3
  case x :: xs => 1
}
// listele sunt orientate obiect
l.head // 1
l.tail //
 
l.map(_*2) // transformare per element
l.foldRight(0)(_ + _)
// 1 + (2 + (3 + acc)))
 
l.foldLeft(Nil:List[Int])((acc,x) => x :: acc)
// (acc op 1) op 2) op 3
 
val lp = List((1,2), (3,1), (4,4))
 
lp.sortBy(_._2)
lp.sortBy(x => x._1 + x._2)
 
/* notatia infix*/
 
trait Nat {
  def +(other: Nat): Nat
  def -(other: Nat): Nat
}
 
case object Zero extends Nat {
  override def +(other: Nat): Nat = other
  override def -(other: Nat): Nat = Zero
}
 
case class Succ(n: Nat) extends Nat {
  override def +(other: Nat): Nat = Succ(n + other)
  override def -(other: Nat): Nat =
    other match {
      case Zero => this
      case Succ(np) => n - np
    }
}
 
val x = Succ(Zero)
val y = Succ(Succ(Zero))
 
x + y
 
 
/*
In Scala, expresia:
 
token1 token2 token3 se citeste:
token1.token2(token3), cu conditia ca:
  token1 sa fie un obiect
  token2 sa fie o functie membru a clasei obiectului resp.
  token3 sa fie un parametru
 */
 
x.-(y)
x - y
 
List(1,2,3) map (_+1)
List(1,2,3).map(_+1)
 
val f = ((x: Int) => x + 1) andThen
                      (_*2) andThen
                      (_-1)
 
f(1)
 
val emails =
  List("matei@gmail.com", "mihai@gmail.com", "ana@gmail.com", "john@yahoo.com", "mary@aol.com", "maria@upb.ro")
 
/* vrem sa aflam frecventa cu care apare un domeniu in lista de mai sus */
/* sa zicem ca ne intereseaza frecventa pt domeniul gmail */
 
// Cum implementam (abordam) aceasta problema FUNCTIONAL?
 
"Test".toList
 
/*
This function will be included later in the final implementation:
 
def getDomain(email: List[Char]): List[Char] =
  email match {
    case '@' :: xs => xs
    case _ :: xs => getDomain(xs)
  }
 
getDomain("Matei@gmail.com".toList)
*/
type Str = List[Char]
 
def getFrequencies(emails: List[String]): List[(String,Int)] = {
 
  def getDomain(email: Str): Str =
    email match {
      case '@' :: xs => xs
      case _ :: xs => getDomain(xs)
    }
 
  def join(domain: Str): String =
    domain.foldRight("")(_ + _) // introducerea unui caracter intr-un string
  /*
  recursive variant for groupLists. We prefer writing is using foldRight, as below:
  def groupLists(l: List[String]): List[List[String]] =
    l match {
      case Nil => Nil
      case x :: xs => {
        val r = groupLists(xs)
        if (x == r.head.head)  (x :: r.head) :: r.tail
        else (x :: Nil) :: r
      }
    }*/
  def groupLists(l: List[String]): List[List[String]] =
    l.foldRight(Nil:List[List[String]])((domain,acc) =>
      if (acc == Nil) List(domain)::Nil
      else if (domain == acc.head.head) (domain :: acc.head) :: acc.tail
      else (domain :: Nil) :: acc
    )
 
// the outer set of parentheses are mandatory, in order to express where does the andThen sequence end
  val sequence =
    (((l: List[String]) => l.map(_.toList))               // make each String into a Str (List[Char])
      andThen (_.map(getDomain(_)))                       // extract the domain from each Str
      andThen (_.map(join(_)))                            // convert to String back again
      andThen (_.sortBy(x => x))                          // sort the list of domains
      andThen (groupLists(_))                             // group identical domains
      andThen (_.map(group => (group.head,group.size))))  // transform each group into a frequency pair
 
  sequence(emails)
 
 
}
 
getFrequencies(emails)
 
 
/*
def groupLists(l: List[String]): List[List[String]] =
  l match {
    case x :: Nil => List(x) :: Nil
    case x :: xs => {
      val r = groupLists(xs)
      if (x == r.head.head) (x :: r.head) :: r.tail
      else (x :: Nil) :: r
    }
  }
*/