Pregunta

Soy nuevo en Scala, y por lo que entiendo ceder en Scala no es como el rendimiento en C #, que es más como de selección.

¿Tiene Scala tiene algo similar a un rendimiento de C # 's? rendimiento de C # 's es grande, ya que hace que escribir iteradores muy fácil.

Actualización: aquí es un ejemplo de código de pseudo desde C # Me gustaría ser capaz de implementar en Scala:

public class Graph<T> {
   public IEnumerable<T> BreadthFirstIterator() {
      List<T> currentLevel = new List<T>();
      currentLevel.add(_root);

      while ( currentLevel.count > 0 ) {
         List<T> nextLevel = new List<T>();
         foreach( var node in currentLevel ) {
            yield return node;
            nextLevel.addRange( node.Children );
         }
         currentLevel = nextLevel;
      }
   }
}

Este código implementa una amplitud iterativo primero recorrido de un gráfico, con el rendimiento, devuelve un iterador, de modo que las personas que llaman pueden atravesar la gráfica usando un habitual para el bucle, por ejemplo:.

graph.BreadthFirstIterator().foreach( n => Console.WriteLine( n ) );

En C #, el rendimiento es simplemente azúcar sintáctica para que sea fácil de escribir un iterador (IEnumerable<T> en .Net, similar a Iterable en Java). Como un iterador, su evaluada perezosamente.

Actualización II: Podría estar equivocado, pero creo que todo el punto de rendimiento en C # es para que usted no tiene que escribir una función de orden superior. P.ej. puede escribir un habitual para bucle o utilizar un método como select / map / filter / where en lugar de pasar en una función que luego atravesar la secuencia.

por ejemplo. graph.iterator().foreach(n => println(n)) en lugar de graph.iterator( n => println(n)).

De esta manera se les puede encadenar fácilmente, por ejemplo graph.iterator().map(x => x.foo).filter(y => y.bar >= 2).foreach(z => println(z)).

¿Fue útil?

Solución 3

Creo que la respuesta (salvo cambios en 2.8) es que la respuesta es no, Scala no tiene azúcar sintáctico similar al rendimiento de C # 's para escribir iteradores (implementaciones de IEumerable o Iterable).

Sin embargo, en la Scala en su lugar podría conseguir un resultado similar mediante introducción de una función para el recorrido que se invocaría en cada artículo en el recorrido. Este enfoque también podría ser implementado en la misma forma en C #.

Así es como me gustaría escribir Traverse en C # sin el uso de rendimiento:

public class Graph<T> {
   public void BreadthFirstTraversal( Action<T> f) {
      List<T> currentLevel = new List<T>();
      currentLevel.add(_root);

      while ( currentLevel.count > 0 ) {
         List<T> nextLevel = new List<T>();
         foreach( var node in currentLevel ) {
            f(node);
            nextLevel.addRange( node.Children );
         }
         currentLevel = nextLevel;
      }
   }
}

A continuación, puede utilizar de esta manera:

graph.BreadthFirstTraversal( n => Console.WriteLine( n ) );

O como esto:

graph.BreadthFirstTraversal( n =>
{
   Console.WriteLine(n);
   DoSomeOtherStuff(n);
});

Otros consejos

El secuestro de la palabra rendimiento aquí distrae de su intención de costumbre: como un marcador de entrada / salida en un corrutina . El C # BreadthFirstIterator en el ejemplo anterior parece utilizar yield en su sentido corrutina; después de un valor es devuelto por yield, la siguiente llamada a BreadthFirstIterator de IEnumerable activa continuará con la siguiente sentencia después yield.

En C #, yield es acoplado a la idea de la iteración en lugar de ser un estado de flujo de control más general, pero dentro de ese dominio limitado su comportamiento es el de un co-rutina. Scala de continuaciones delimitados puede permitir que uno de definir corrutinas. Hasta entonces, Scala carece de una tal capacidad, especialmente teniendo en cuenta su significado alternativo para yield.

Si lo hace, es posible que desee considerar esta cuestión de la respuesta: ¿Cuál es el rendimiento de Scala?

Aquí está la documentación de Scala para este tipo de constructo: http://www.scala-lang.org/node/111

ACTUALIZACIÓN:

Este blog habla sobre C # rendimiento y la Scala: http: //hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-1-mixins-and-traits.html

Se entra en algunos detalles acerca de cómo se utilizan las extensiones para hacer el trabajo IEnumerable en comparación con el uso de rasgos en Scala.

Por lo tanto, está en lo cierto que el rendimiento no funcionará de la misma manera en la Scala como C #, pero eso es porque son muy diferentes, y por lo que si quieres hacer esto BreadthFirst como un rasgo continuación, puede llamar a la map() y filter y foreach métodos, tal como lo haría en C #, pero el rasgo ayudará a resolver el problema de cómo atravesar la colección.

A pesar de Scala tiene una yield de palabras clave, que es bastante diferente de la de C # yield y yield de Ruby es diferente de ambos. Parece ser una palabra clave violentamente en exceso. El uso de yield en C # parece muy limitada a primera vista.

Para hacer lo mismo en Scala, se podría definir su propia función de orden superior. En Inglés, esto significa que una función que toma una función como parámetro.

Para tomar de Microsoft ejemplo, aquí está un método Scala:

object Powers {
  def apply(number:Int, exponent:Int) (f:(Double) => Any) = {
    (new Range(1,exponent+1,1)).map{exponent => f(Math.pow(number, exponent))}
  }
}

Ahora que tenemos un "repetidor":

scala> Powers(2,8){ println(_) }
2.0
4.0
8.0
16.0
32.0
64.0
128.0
256.0

Notas:

  • Powers(2,8) es el mismo que Powers.apply(2,8). Eso es sólo un truco compilador.
  • Este método se define con dos listas de parámetros, que pueden ser confuso. Simplemente le permite hacer: Powers(2, 8){ println(_) } en lugar de Powers(2, 8, {println(_)})

Scala: 1, C #: 0


Actualización:

Para su ejemplo que acabamos de agregado, escribir traverse que hace el recorrido que desee sin pensar en cómo va a utilizarlo. A continuación, añadir un parámetro adicional mediante la adición de (f(Node) => Any) después de la lista de parámetros traverse, por ejemplo.

def traverse(node:Node, maxDepth:Int)(f(Node) => Any)) { ... }

En el punto en traverse donde se tiene una valor desde el que yield con en C #, llame f(yieldValue).

Cuando se desea utilizar este "iterador" traverse llamada y pasar una función a él que hace lo que sea que quieres hacer para cada elemento del iterador.

traverse(node, maxDepth) { (yieldValue) =>
  // this is f(yieldValue) and will be called for each value that you call f with
  println(yieldValue)
}

Este es un caso básico para la "programación funcional" y usted debe asegurarse de que entiende que sea un éxito con Scala.

Puede hacer esto en Scala> = 2,8 usando una implementación de generadores en términos de continuaciones delimitados. Tendrá que continuaciones del href="http://blog.richdougherty.com/search/label/continuations" rel="nofollow plugin de y luego algo en este sentido,

import scala.continuations._
import scala.continuations.ControlContext._

object Test {

  def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
    if (cond) {
      body
      loopWhile(cond)(body)
    } else ()
  }

  abstract class Generator[T] {
    var producerCont : (Unit => Unit) = null
    var consumerCont : (T => Unit) = null

    protected def body : Unit @suspendable

    reset {
      body
    }

    def generate(t : T) : Unit @suspendable =
      shift {
        (k : Unit => Unit) => {
          producerCont = k
          if (consumerCont != null)
            consumerCont(t)
        }
      }

    def next : T @suspendable =
      shift {
        (k : T => Unit) => {
          consumerCont = k
          if (producerCont != null)
            producerCont()
        }
      }
  }

  def main(args: Array[String]) {
    val g = new Generator[Int] {
      def body = {
        var i = 0
        loopWhile(i < 10) {
          generate(i)
          i += 1
        }
      }
    }

    reset {
      loopWhile(true) {
        println("Generated: "+g.next)
      }
    }
  }
}

Como ya se ha mencionado que podría crear un generador utilizando el continuaciones-plugin para crear un rendimiento que es exactamente comportando como C #:

import scala.util.continuations._

object GenTest {

    val gen = new Generator[Int] { def produce = {
        yieldValue(1)
        yieldValue(2)
        yieldValue(3)
        Thread.sleep(1000)
        yieldValue(42)
  }}


    def main(args: Array[String]): Unit = {
        for (v <- gen) {
            println(v)
        }
    }
}

abstract class Generator[E] {

    var loopFn: (E => Unit) = null

    def produce(): Unit @cps[Unit]

  def foreach(f: => (E => Unit)): Unit = {
        loopFn = f
        reset[Unit,Unit]( produce )
  }

  def yieldValue(value: E): Unit @cps[Unit] =
    shift { genK: (Unit => Unit) =>
      loopFn( value )
      genK( () )
      ()
    }

}

Viniendo de un fondo # C y haber depurado el código Scala de hotzen (adaptado a Scala 2.11.6), debo decir que este uso continuaciones se acerca a la C # -Rendimiento equivalente. No sé si continuaciones todavía funcionarían de forma similar si se necesitaban múltiples generadores, corriendo todos en los mismos métodos o, posiblemente, repartidas en diferentes métodos, pero estoy feliz existen continuaciones, por lo que no estoy obligado a trabajar con múltiples hilos para lograr similar o pase a lo largo de devoluciones de llamadas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top