我是新来斯卡拉,并从我的理解产生在Scala中并不像在C#中的产量,它更像是选择。
斯卡拉是否有类似C#的产量些什么呢? C#的产量是很大的,因为它使得编写迭代器非常容易。
更新:这是从C#伪代码的例子,我希望能在斯卡拉实现:
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;
}
}
}
此代码实现的曲线图的迭代广度优先遍历,使用产率,它返回一个迭代,以便呼叫者可以使用用于循环,例如一个常规的遍历图形:
graph.BreadthFirstIterator().foreach( n => Console.WriteLine( n ) );
在C#,产率仅仅是语法糖,以可以很容易地写出(迭代器IEnumerable<T>
NET中,类似于Iterable
中的Java)。 作为一个迭代器,它懒洋洋地评估。
更新二:我可能是错在这里,但我想在C#产量的整点是,这样你就不必写一个高阶函数。 例如,你可以写一个正for循环或使用方法等select
/ map
/ filter
/ where
而不是传递函数中然后将遍历序列。
例如graph.iterator().foreach(n => println(n))
而不是graph.iterator( n => println(n))
。
这种方式可以很容易地把它们连,例如graph.iterator().map(x => x.foo).filter(y => y.bar >= 2).foreach(z => println(z))
Answer 1:
字产量的劫持从其通常意图在这里分心:如在入口/出口标志协程 。 的C# BreadthFirstIterator
在上述示例似乎使用yield
在其协程感; 值由返回后yield
,以激活下一个电话BreadthFirstIterator
的IEnumerable
将继续后面的语句yield
。
在C#, yield
被耦合到迭代的想法 ,而不是更一般控制流语句,但该有限域中的其行为是一个协程的。 Scala的分隔延续可以允许一个定义协同程序。 在此之前,Scala中缺乏这样的能力,尤其是考虑到它的备用含义yield
。
Answer 2:
是的,它不,你可能想看看这个问题的答案: 什么是Scala的收益呢?
这里是斯卡拉的文档这种类型的构造: http://www.scala-lang.org/node/111
更新:
这是关于C#的产量和Scala博客会谈: http://hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-1-mixins-and-traits.html
他走进如何扩展被用来制造IEnumerable的工作相比,在斯卡拉使用性状一些细节。
所以,你是正确的,产量将不能运行在斯卡拉为C#以同样的方式,但那是因为他们有很大的不同,所以如果你想这样做BreadthFirst的特质,那么你可以调用map()
和filter
和foreach
方法,就像您在C#中,但特质将帮助解决如何遍历集合的问题。
Answer 3:
我想答案(2.8禁止改变)是,答案是否定的,Scala没有类似于C#的产量语法糖写迭代器(IEumerable或可迭代的实现)。
然而,在Scala中可以转而实现通过一个函数传递给它会调用在遍历每个项目的遍历类似的结果。 这种方法也可以在C#中的相同的方式实现。
这里是我会写导线在C#中不使用产量的:
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;
}
}
}
然后,您可以使用这样的:
graph.BreadthFirstTraversal( n => Console.WriteLine( n ) );
或者是这样的:
graph.BreadthFirstTraversal( n =>
{
Console.WriteLine(n);
DoSomeOtherStuff(n);
});
Answer 4:
尽管Scala有一个关键字yield
,这是从C#完全不同的yield
和Ruby的yield
是从两个不同的。 这似乎是一个疯狂滥用关键字。 采用yield
在C#乍一看非常有限。
做同样在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))}
}
}
现在,你有你的“迭代”:
scala> Powers(2,8){ println(_) }
2.0
4.0
8.0
16.0
32.0
64.0
128.0
256.0
笔记:
-
Powers(2,8)
是相同的Powers.apply(2,8)
这只是一个编译器的把戏。 - 这种方法有两个参数列表,这可能会造成混淆定义。 它只是让你做:
Powers(2, 8){ println(_) }
,而不是Powers(2, 8, {println(_)})
比例:1,C#:0
更新:
为了您刚刚添加的例子,写traverse
,你想要做而不考虑你将如何使用它遍历。 然后通过加入添加一个额外的参数(f(Node) => Any)
的后traverse
参数列表,例如
def traverse(node:Node, maxDepth:Int)(f(Node) => Any)) { ... }
在点traverse
,你有一个值,你会yield
在C#中,调用f(yieldValue)
当你想用这个“迭代”呼叫traverse
并通过一个函数,该做什么是你想要在迭代每个元素做。
traverse(node, maxDepth) { (yieldValue) =>
// this is f(yieldValue) and will be called for each value that you call f with
println(yieldValue)
}
这是“函数编程”一个基本的情况下,你应该确保你明白它是成功的斯卡拉。
Answer 5:
您可以使用生成的实现在分隔的延续方面斯卡拉> = 2.8做到这一点。 你需要的延续插件沿着这些路线,然后东西,
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)
}
}
}
}
Answer 6:
前面已经提到,你可以使用延续,插件创建一个收益率为正好表现得就像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( () )
()
}
}
Answer 7:
从C#背景的并具有调试从hotzen(适用于斯卡拉2.11.6)的Scala代码,我必须说,这延续使用率接近了C#-yield等同。 我不知道是否延续仍然会发挥同样的,如果是需要多个发电机,运行都在同一个方法或可能分布在不同的方法,但我很高兴延续确实存在,所以我不是被迫与多线程工作,以实现类似的,或沿回调通。
文章来源: Does Scala have an equivalent to C# yield?