Permalink
Browse files

Merge branch 'master' of https://github.com/scalaz/scalaz

  • Loading branch information...
2 parents 5b7198f + 185914d commit 2716437e355b0d3213e8c4de74e3aa7d12e2c419 @runarorama runarorama committed Mar 29, 2012
@@ -112,6 +112,11 @@ sealed trait ListW[A] extends PimpedType[List[A]] {
}
def pairs: List[(A, A)] = (value: ListW[A]).tails.tail >>= (value.zip(_))
+
+ def groupNelBy[K](f : A => K) : Map[K, NonEmptyList[A]] = (Map.empty[K, NonEmptyList[A]] /: value) { (m, a) =>
+ val k = f(a)
+ m + (k -> (m.get(k).map(a <:: _) | nel(a)))
+ }
}
trait Lists {
@@ -72,6 +72,10 @@ sealed trait NonEmptyList[+A] {
def maxBy[B](f: A B)(implicit cmp: scala.Ordering[B]): A =
tail.foldLeft(head) { (a, b) if (cmp.gteq(f(a), f(b))) a else b }
+ def foldl1Nel[B >: A](f : (B, A) => B) : B = ((head : B) /: tail) { f }
+
+ def sumNel[B >: A](implicit s : Semigroup[B]) : B = foldl1Nel[B]( (a1, a2) => s.append(a1, a2) )
+
override def toString: String = "NonEmpty" + (head :: tail)
override def equals(any: Any): Boolean =
@@ -79,32 +79,33 @@ sealed trait Zipper[+A] {
def insertRight[AA >: A](y: AA): Zipper[AA] = zipper(focus #:: lefts, y, rights)
/**
- * An alias for deleteRight
+ * An alias for `deleteRight`
*/
def delete: Option[Zipper[A]] = deleteRight
/**
* Deletes the element at focus and moves the focus to the left. If there is no element on the left,
* focus is moved to the right.
*/
- def deleteLeft: Option[Zipper[A]] = rights match {
- case Stream.Empty => None
- case r #:: rs => Some(lefts match {
- case Stream.Empty => zipper(Stream.Empty, r, rs)
- case l #:: ls => zipper(ls, l, rights)
- })
+ def deleteLeft: Option[Zipper[A]] = lefts match {
+
+ case l #:: ls => Some(zipper(ls, l, rights))
+ case Stream.Empty => rights match {
+ case r #:: rs => Some(zipper(Stream.empty, r, rs))
+ case Stream.Empty => None
+ }
}
/**
* Deletes the element at focus and moves the focus to the right. If there is no element on the right,
* focus is moved to the left.
*/
def deleteRight: Option[Zipper[A]] = rights match {
- case Stream.Empty => None
- case r #:: rs => Some(lefts match {
- case Stream.Empty => zipper(Stream.Empty, r, rs)
- case l #:: ls => zipper(ls, l, rights)
- })
+ case r #:: rs => Some(zipper(lefts, r, rs))
+ case Stream.Empty => lefts match {
+ case l #:: ls => Some(zipper(ls, l, Stream.empty))
+ case Stream.Empty => None
+ }
}
/**
@@ -233,34 +234,28 @@ sealed trait Zipper[+A] {
* Deletes the focused element and moves focus to the left. If the focus was on the first element,
* focus is moved to the last element.
*/
- def deleteLeftC: Option[Zipper[A]] = rights match {
- case Stream.Empty => None
- case _ #:: _ => Some(lefts match {
- case l #:: ls => zipper(ls, l, rights)
- case Stream.Empty => {
- val r = rights.reverse
- zipper(r.tail, r.head, Stream.Empty)
- }
- })
+ def deleteLeftC: Option[Zipper[A]] = lefts match {
+ case l #:: ls => Some(zipper(ls, l, rights))
+ case Stream.Empty => rights match {
+ case _ #:: _ => val rrev = rights.reverse; Some(zipper(rrev.tail, rrev.head, Stream.empty))
+ case Stream.Empty => None
+ }
}
/**
* Deletes the focused element and moves focus to the right. If the focus was on the last element,
* focus is moved to the first element.
*/
- def deleteRightC: Option[Zipper[A]] = lefts match {
- case Stream.Empty => None
- case _ #:: _ => Some(rights match {
- case r #:: rs => zipper(lefts, r, rs)
- case Stream.Empty => {
- val l = lefts.reverse
- zipper(Stream.Empty, l.head, l.tail)
- }
- })
+ def deleteRightC: Option[Zipper[A]] = rights match {
+ case r #:: rs => Some(zipper(lefts, r, rs))
+ case Stream.Empty => lefts match {
+ case _ #:: _ => val lrev = lefts.reverse; Some(zipper(Stream.empty, lrev.head, lrev.tail))
+ case Stream.Empty => None
+ }
}
/**
- * An alias for deleteRightC
+ * An alias for `deleteRightC`
*/
def deleteC: Option[Zipper[A]] = deleteRightC
}
@@ -33,6 +33,20 @@ class ListWTest extends Specification with Sugar with ScalaCheck {
(a: List[Int], b: List[Int]) => (a.intercalate(b) ≟ intercalate(a, b))
}
+ "groupNelBy produces NonEmptyLists of the correct number of elements" verifies {
+ (ss: List[String]) => {
+ val grouped = ss.groupNelBy(_.length)
+ grouped.values.foldMap(_.count) === ss.length
+ }
+ }
+
+ "groupNelBy produces NonEmptyLists of the correct elements" verifies {
+ (ss: List[String]) => {
+ val grouped = ss.groupNelBy(_.length)
+ grouped.values.map(_.list).flatten.toSet === ss.toSet
+ }
+ }
+
"groupByM joined (in Identity) produces the same list" verifies {
(a: List[Int], p: (Int, Int) => Boolean) =>
a.groupByM[Identity]((a, b) => p(a, b).η[Identity]).value.join === a
@@ -1,9 +1,9 @@
package scalaz
-import org.specs.Specification
+import org.specs.{Sugar, Specification, ScalaCheck}
import Scalaz._
-class NonEmptyListTest extends Specification {
+class NonEmptyListTest extends Specification with Sugar with ScalaCheck {
"non empty list" should {
"support min" in {
@@ -38,5 +38,16 @@ class NonEmptyListTest extends Specification {
nel("foo", "a", "scalaz", "scala").maxBy(_.size) must_== "scalaz"
}
}
+ "sumNel produces sum of NonEmptyList" verifies {
+ (is: List[Int]) => {
+ !(is.toNel map (_.sumNel === is.sum) exists (x => !x))
+ }
+ }
+ "fold1Nel produces fold of NonEmptyList" verifies {
+ (is: List[Int]) => {
+ !(is.toNel map (_.foldl1Nel(_ + _) === is.sum) exists (x => !x))
+ }
+ }
}
+
}
@@ -26,6 +26,48 @@ object ZipperSpec extends Properties("Zipper") {
zn.rights.length === z.rights.length + 1) getOrElse (xs.length < 2)
})
+ property("DeleteRight Affects Lengths") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.toZipper;
+ zn <- z.deleteRight)
+ yield zn.rights.length === z.rights.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteRightC Affects Lengths") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.toZipper;
+ zn <- z.deleteRightC)
+ yield zn.rights.length === z.rights.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteRight Affects Lengths and Moves Left if at end") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.zipperEnd;
+ zn <- z.deleteRight)
+ yield zn.lefts.length === z.lefts.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteLeft Affects Lengths") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.zipperEnd;
+ zn <- z.deleteLeft)
+ yield zn.lefts.length === z.lefts.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteLeftC Affects Lengths") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.zipperEnd;
+ zn <- z.deleteLeftC)
+ yield zn.lefts.length === z.lefts.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteLeft Affects Lengths and Moves Right if at start") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.toZipper;
+ zn <- z.deleteLeft)
+ yield zn.rights.length === z.rights.length - 1) getOrElse (xs.length < 2)
+ })
+
+ property("DeleteRightC Affects Lengths and Cycles to Start if at end") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.zipperEnd;
+ zn <- z.deleteRightC)
+ yield zn.rights.length === z.lefts.length - 1) getOrElse (xs.length < 2)
+ })
+ property("DeleteLeftC Affects Lengths and Cycles to end if at start") = forAll((xs: Stream[Int]) => {
+ (for (z <- xs.toZipper;
+ zn <- z.deleteLeftC)
+ yield zn.lefts.length === z.rights.length - 1) getOrElse (xs.length < 2)
+ })
+
property("Move") = forAll((xs: Stream[Int], ys: Stream[Int], f: Int, n: Short) =>
(zipper(xs, f, ys).move(n) map ((z: Zipper[Int]) =>
z.lefts.length === xs.length + n &&

0 comments on commit 2716437

Please sign in to comment.