Skip to content

Instantly share code, notes, and snippets.

@HeartSaVioR
Last active August 29, 2015 14:17
Show Gist options
  • Save HeartSaVioR/78ab19710f9d78f91392 to your computer and use it in GitHub Desktop.
Save HeartSaVioR/78ab19710f9d78f91392 to your computer and use it in GitHub Desktop.
Functional Programming in Scala (chap 3)
def tail[A](lst: List[A]): List[A] = lst match {
case Nil => Nil
case x :: xs => xs
}
// solution in a book
// https://github.com/fpinscala/fpinscala/blob/master/answerkey/datastructures/02.answer.scala
/*
// Although we could return `Nil` when the input list is empty, we choose to throw an exception instead. This is a somewhat subjective choice. In our experience, taking the tail of an empty list is often a bug, and silently returning a value just means this bug will be discovered later, further from the place where it was introduced.
// It's generally good practice when pattern matching to use `_` for any variables you don't intend to use on the right hand side of a pattern. This makes it clear the value isn't relevant.
def tail[A](l: List[A]): List[A] =
l match {
case Nil => sys.error("tail of empty list")
case Cons(_,t) => t
}
*/
def setHead[A](lst: List[A], head: A): List[A] = lst match {
case Nil => head :: Nil
case x :: xs => head :: xs
}
// solution in a book
// https://github.com/fpinscala/fpinscala/blob/master/answerkey/datastructures/03.answer.scala
/*
// If a function body consists solely of a match expression, we'll often put the match on the same line as the function signature, rather than introducing another level of nesting.
def setHead[A](l: List[A], h: A): List[A] = l match {
case Nil => sys.error("setHead on empty list")
case Cons(_,t) => Cons(h,t)
}
*/
def drop[A](l: List[A], n: Int): List[A] = {
if (n <= 0)
l
else
l match {
case Nil => Nil
case x :: xs => drop(xs, n - 1)
}
}
// solution in a book
// https://github.com/fpinscala/fpinscala/blob/master/answerkey/datastructures/04.answer.scala
/*
// Again, it's somewhat subjective whether to throw an exception when asked to drop more elements than the list contains. The usual default for `drop` is not to throw an exception, since it's typically used in cases where this is not indicative of a programming error. If you pay attention to how you use `drop`, it's often in cases where the length of the input list is unknown, and the number of elements to be dropped is being computed from something else. If `drop` threw an exception, we'd have to first compute or check the length and only drop up to that many elements.
def drop[A](l: List[A], n: Int): List[A] =
if (n <= 0) l
else l match {
case Nil => Nil
case Cons(_,t) => drop(t, n-1)
}
*/
def dropWhile[A](l: List[A], f: A => Boolean): List[A] = {
l match {
case Nil => Nil
case x :: xs => if (f(x)) dropWhile(xs, f) else l
}
}
// solution in a book
// https://github.com/fpinscala/fpinscala/blob/master/answerkey/datastructures/05.answer.scala
/*
// Somewhat overkill, but to illustrate the feature we're using a _pattern guard_,
// to only match a `Cons` whose head satisfies our predicate, `f`.
// The syntax is to add `if <cond>` after the pattern, before the `=>`,
// where `<cond>` can use any of the variables introduced by the pattern.
def dropWhile[A](l: List[A], f: A => Boolean): List[A] =
l match {
case Cons(h,t) if f(h) => dropWhile(t, f)
case _ => l
}
*/
def init[A](l: List[A]): List[A] = l match {
case Nil => Nil
case x :: Nil => Nil
case x :: xs => x :: init(xs)
}
// solution in a book
// https://github.com/fpinscala/fpinscala/blob/master/answerkey/datastructures/06.answer.scala
/*
// Note that we're copying the entire list up until the last element. Besides being inefficient, the natural recursive solution will use a stack frame for each element of the list, which can lead to stack overflows for large lists (can you see why?). With lists, it's common to use a temporary, mutable buffer internal to the function (with lazy lists or streams, which we discuss in chapter 5, we don't normally do this). So long as the buffer is allocated internal to the function, the mutation is not observable and RT is preserved.
// Another common convention is to accumulate the output list in reverse order, then reverse it at the end, which doesn't require even local mutation. We'll write a reverse function later in this chapter.
def init[A](l: List[A]): List[A] =
l match {
case Nil => sys.error("init of empty list")
case Cons(_,Nil) => Nil
case Cons(h,t) => Cons(h,init(t))
}
def init2[A](l: List[A]): List[A] = {
import collection.mutable.ListBuffer
val buf = new ListBuffer[A]
@annotation.tailrec
def go(cur: List[A]): List[A] = cur match {
case Nil => sys.error("init of empty list")
case Cons(_,Nil) => List(buf.toList: _*)
case Cons(h,t) => buf += h; go(t)
}
go(l)
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment