spark scala recursion fold idiomatic

scala - spark - Construcción idiomática para verificar si una colección está ordenada



foldleft spark (9)

En caso de que se haya perdido la solución elegante de missingfaktor en los comentarios above :

(l, l.tail).zipped.forall(_ <= _)

Esta solución es muy legible y saldrá en el primer elemento fuera de servicio.

Con la intención de aprender y profundizar en esta question , sigo sintiendo curiosidad por las alternativas idiomáticas a la recursión explícita para un algoritmo que verifica si se ordena una lista (o colección). (Estoy simplificando las cosas aquí usando un operador para comparar e Int como tipo; me gustaría ver el algoritmo antes de profundizar en los genéricos)

La versión recursiva básica sería (por @Luigi Plinge):

def isOrdered(l:List[Int]): Boolean = l match { case Nil => true case x :: Nil => true case x :: xs => x <= xs.head && isOrdered(xs) }

Una forma idiomática deficiente sería:

def isOrdered(l: List[Int]) = l == l.sorted

Un algoritmo alternativo que usa fold:

def isOrdered(l: List[Int]) = l.foldLeft((true, None:Option[Int]))((x,y) => (x._1 && x._2.map(_ <= y).getOrElse(true), Some(y)))._1

Tiene el inconveniente de que se comparará para todos los n elementos de la lista, incluso si pudiera detenerse antes después de encontrar el primer elemento fuera de servicio. ¿Hay alguna manera de "detener" el doblez y por lo tanto hacer de esta una mejor solución?

¿Alguna otra alternativa (elegante)?


Esto saldrá después del primer elemento que está fuera de servicio. Por lo tanto, debería funcionar bien, pero no he probado eso. También es mucho más elegante en mi opinión. :)

def sorted(l:List[Int]) = l.view.zip(l.tail).forall(x => x._1 <= x._2)


La versión recursiva está bien, pero limitada a List (con cambios limitados, funcionaría bien en LinearSeq ).

Si se implementó en la biblioteca estándar (tendría sentido), probablemente se haría en IterableLike y tendría una implementación completamente imperativa (ver, por ejemplo, el método find )

Puede interrumpir foldLeft con un return (en cuyo caso solo necesita el elemento anterior y no boolean todo el tiempo)

import Ordering.Implicits._ def isOrdered[A: Ordering](seq: Seq[A]): Boolean = { if (!seq.isEmpty) seq.tail.foldLeft(seq.head){(previous, current) => if (previous > current) return false; current } true }

pero no veo cómo es mejor o incluso idiomático que una implementación imperativa. No estoy seguro de que no lo llamaría imperativo en realidad.

Otra solución podría ser

def isOrdered[A: Ordering](seq: Seq[A]): Boolean = ! seq.sliding(2).exists{s => s.length == 2 && s(0) > s(1)}

Más bien conciso, y tal vez eso podría llamarse idiomático, no estoy seguro. Pero creo que no está muy claro. Además, todos estos métodos probablemente funcionarían mucho peor que la versión imperativa o recursiva de la cola, y no creo que tengan ninguna claridad adicional que pueda comprar eso.

También deberías echarle un vistazo a esta pregunta .


Me voy con esto, que es bastante similar al de Kim Stebel, como una cuestión de hecho.

def isOrdered(list: List[Int]): Boolean = ( list sliding 2 map { case List(a, b) => () => a < b } forall (_()) )


Para detener la iteración, puede usar Iteratee :

import scalaz._ import Scalaz._ import IterV._ import math.Ordering import Ordering.Implicits._ implicit val ListEnumerator = new Enumerator[List] { def apply[E, A](e: List[E], i: IterV[E, A]): IterV[E, A] = e match { case List() => i case x :: xs => i.fold(done = (_, _) => i, cont = k => apply(xs, k(El(x)))) } } def sorted[E: Ordering] : IterV[E, Boolean] = { def step(is: Boolean, e: E)(s: Input[E]): IterV[E, Boolean] = s(el = e2 => if (is && e < e2) Cont(step(is, e2)) else Done(false, EOF[E]), empty = Cont(step(is, e)), eof = Done(is, EOF[E])) def first(s: Input[E]): IterV[E, Boolean] = s(el = e1 => Cont(step(true, e1)), empty = Cont(first), eof = Done(true, EOF[E])) Cont(first) } scala> val s = sorted[Int] s: scalaz.IterV[Int,Boolean] = scalaz.IterV$Cont$$anon$2@5e9132b3 scala> s(List(1,2,3)).run res11: Boolean = true scala> s(List(1,2,3,0)).run res12: Boolean = false


Por "idiomático", supongo que estás hablando de "Idioms" de McBride y Paterson en su papel Applicative Programming With Effects . : o)

Así es como usaría sus expresiones idiomáticas para verificar si se ordena una colección:

import scalaz._ import Scalaz._ case class Lte[A](v: A, b: Boolean) implicit def lteSemigroup[A:Order] = new Semigroup[Lte[A]] { def append(a1: Lte[A], a2: => Lte[A]) = { lazy val b = a1.v lte a2.v Lte(if (!a1.b || b) a1.v else a2.v, a1.b && b && a2.b) } } def isOrdered[T[_]:Traverse, A:Order](ta: T[A]) = ta.foldMapDefault(x => some(Lte(x, true))).fold(_.b, true)

Así es como funciona esto:

Cualquier estructura de datos T[A] donde exista una implementación de Traverse[T] , puede atravesarse con un functor Applicative , o "modismo", o "functor monoidal débilmente laxo". Simplemente sucede que cada Monoid induce tal modismo de forma gratuita (ver la sección 4 del documento).

Un monoide es solo una operación binaria asociativa sobre algún tipo, y un elemento de identidad para esa operación. Estoy definiendo un Semigroup[Lte[A]] (un semigrupo es lo mismo que un monoide, excepto sin el elemento de identidad) cuya operación asociativa rastrea el menor de dos valores y si el valor de la izquierda es menor que el correcto. Y, por supuesto, la Option[Lte[A]] es solo el monoide generado libremente por nuestro semigrupo.

Finalmente, foldMapDefault atraviesa el tipo de colección T en el modismo inducido por el monoide. El resultado b contendrá verdadero si cada valor fue menor que todos los siguientes (es decir, se ordenó la colección), o None si la T no tenía elementos. Dado que una T vacía se ordena por convención, pasamos true como el segundo argumento al fold final de la Option .

Como beneficio adicional, esto funciona para todas las colecciones atravesables. Una demostración

scala> val b = isOrdered(List(1,3,5,7,123)) b: Boolean = true scala> val b = isOrdered(Seq(5,7,2,3,6)) b: Boolean = false scala> val b = isOrdered(Map((2 -> 22, 33 -> 3))) b: Boolean = true scala> val b = isOrdered(some("hello")) b: Boolean = true

Una prueba:

import org.scalacheck._ scala> val p = forAll((xs: List[Int]) => (xs /== xs.sorted) ==> !isOrdered(xs)) p:org.scalacheck.Prop = Prop scala> val q = forAll((xs: List[Int]) => isOrdered(xs.sorted)) q: org.scalacheck.Prop = Prop scala> p && q check + OK, passed 100 tests.

Y así es como haces un recorrido idiomático para detectar si se ordena una colección.


Si divide la Lista en dos partes, y verifica si la última de la primera parte es más baja que la primera de la segunda parte. Si es así, puedes verificar en paralelo para ambas partes. Aquí la idea esquemática, primero sin paralelo:

def isOrdered (l: List [Int]): Boolean = l.size/2 match { case 0 => true case m => { val low = l.take (m) val high = l.drop (m) low.last <= high.head && isOrdered (low) && isOrdered (high) } }

Y ahora con paralelo, y usando splitAt lugar de take/drop :

def isOrdered (l: List[Int]): Boolean = l.size/2 match { case 0 => true case m => { val (low, high) = l.splitAt (m) low.last <= high.head && ! List (low, high).par.exists (x => isOrdered (x) == false) } }


mi solución se combina con la solución de missingfaktor y el pedido

def isSorted[T](l: Seq[T])(implicit ord: Ordering[T]) = (l, l.tail).zipped.forall(ord.lt(_, _))

y puedes usar tu propio método de comparación. P.ej

isSorted(dataList)(Ordering.by[Post, Date](_.lastUpdateTime))


def isSorted[A <: Ordered[A]](sequence: List[A]): Boolean = { sequence match { case Nil => true case x::Nil => true case x::y::rest => (x < y) && isSorted(y::rest) } } Explain how it works.