22 of 99

From http://aperiodic.net/phil/scala/s-99/.

object NinetyNine {
  // P01  
  def last[A](as: List[A]): A = as match {
    case Nil => error("last on empty list")
    case List(x) => x
    case _ :: xs => last(xs)
  }
 
  // P02
  def penultimate[A](as: List[A]): A = as match {
    case x :: _ :: Nil => x
    case _ => penultimate(as.tail)
  }  
 
  // P03
  def nth[A](n: Int, as: List[A]): A = as match {
    case x :: _ if n == 0 => x
    case _ => nth(n - 1, as.tail)
  }
 
  // P04
  def length[A](as: List[A]) = (0 /: as)((a, _) => a + 1)
 
  // P05
  def reverse[A](as: List[A]) = (List[A]() /: as)((a, b) => b :: a)
 
  // P06
  def isPalindrome[A](as: List[A]) = as == reverse(as)
 
  // P07
  def flatten(as: Any*): List[Any] = as flatMap {
    case (ns: List[Any]) => flatten(ns: _*)
    case (n: Any) => List(n)
  } toList
 
  // List is missing a group method
  implicit def Group[A](as: List[A]): { def group: List[List[A]] } = new {
    def group = as match {
      case Nil => Nil
      case x :: xs => {
        val (a, b) = xs.span(_ == x)
        (x :: a) :: Group(b).group
      }
    }
  }
 
  // P08
  def compress[A](as: List[A]) = as.group map (_.head)
 
  def compress2[A](as: List[A]): List[A] = as match {
    case Nil => Nil
    case x :: xs => x :: compress(xs.dropWhile(_ == x))
  }
 
  // P09
  def pack[A] = (_: List[A]).group
 
  // P10
  def encode[A](as: List[A]) = pack(as) map (k => (k.length, k.head))
 
  // P11
  // ick
  def encodeModified[A](as: List[A]) = pack(as) map (k => {
    val l = k.length
    val h = k.head
    if(l == 1) h else (l, h)
  })
 
  // P12
  // ick
  def decodeModified(as: List[_]) = as flatMap {
    case (n: Int, s) => Stream.make(n, s)
    case s => List(s)
  }
 
  // P13
  // WTF?
  def encode_direct[A] = (_: List[A]).group map (k => (k.length, k.head))
 
  // P14
  def duplicate[A] = (_: List[A]) flatMap (k => List(k, k))
 
  // P15
  def duplicateN[A](n: Int, as: List[A]) = as flatMap (k => Stream.make(n, k))
 
  // P16
  def drop[A](n: Int, as: List[A]) = as.zipWithIndex filter { case (_, x) => x % n != 2 } map (_._1)
 
  // P17
  def split[A](n: Int, as: List[A]) = (as take n, as drop n)
 
  // P18
  def slice[A](x: Int, y: Int, as: List[A]) = as drop x take (y - x)
 
  // P19
  def rotate[A](n: Int, as: List[A]): List[A] =
    if(n < 0) rotate(as.length + n, as)
    else (as drop n) ::: (as take n)
 
  // P20
  def remove_at[A](n: Int, as: List[A]) = {
    val (k, Some(z)) = as.zipWithIndex.foldRight[(List[A], Option[A])]((Nil, None)) { case ((a, x), (t, s)) => if(n == x) (t, Some(a)) else (a :: t, s) }
    (k, z)
  }
 
  // P21
  def insert_at[A](a: A, n: Int, as: List[A]) = {
    val (x, y) = as splitAt n
    x ::: a :: y
  }
 
  // another missing library function
  implicit def Unfold[A](a: A): { def unfold[B](f: A => Option[(B, A)]): List[B] } = new {
    def unfold[B](f: A => Option[(B, A)]) = f(a) match {
      case None => Nil
      case Some((b, z)) => b :: (Unfold(z) unfold f)
    }
  }
 
  // P22
  def range[A](x: Int, y: Int) = x unfold (a => if(a > y) None else Some(a, a + 1))
 
  def main(args: Array[String]) {
    val ps = List(
      last(List(1, 1, 2, 3, 5, 8)), // P01
      penultimate(List(1, 1, 2, 3, 5, 8)), // P02
      nth(2, List(1, 1, 2, 3, 5, 8)), // P03
      length(List(1, 1, 2, 3, 5, 8)), // P04
      reverse(List(1, 1, 2, 3, 5, 8)), // P05
      isPalindrome(List(1, 2, 3, 2, 1)), // P06
      flatten(List(1, 1), 2, List(3, List(5, 8))), // P07
      compress(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)), // P08
      pack(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)), // P09
      encode(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)), // P10
      encodeModified(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)), // P11
      decodeModified(List((4, 'a), 'b, (2, 'c), (2, 'a), 'd, (4, 'e))), // P12
      encode_direct(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)), // P13
      duplicate(List('a, 'b, 'c, 'c, 'd)), // P14
      duplicateN(3, List('a, 'b, 'c, 'c, 'd)), // P15
      drop(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)), // P16
      split(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)), // P17
      slice(3, 7, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)), // P18
      (rotate(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)), rotate(-2, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))), // P19
      remove_at(1, List('a, 'b, 'c, 'd)), // P20
      insert_at('new, 1, List('a, 'b, 'c, 'd)), // P21
      range(4, 9), // P22
    )
 
    ps.zipWithIndex foreach { case (p, n) => println("P" + (n + 1) + ": " + p) }
  }
}

2 Responses to “22 of 99”

  1. liesen Says:

    Nice. Very similar to mine. (For P16 you want to check if index + 1 mod n is not 0, like: xs.zipWithIndex filter { case (_, i) => (i + 1) % n != 0 } map { _._1 })

  2. Phil! Gold Says:

    As I work through and write solutions to these, I’ve tweaked the definitions a little on some of them. The biggest so far is probably to P12, which I switched to take lists as provided by `encode`, not `encodeModified` (thus preserving type information).

Leave a Reply