Scala no tiene un equivalente a C # rendimiento?
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))
.
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 quePowers.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 dePowers(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.