Scala 是否有相当于 C# 的产量?

发布于 2024-08-10 01:27:54 字数 1416 浏览 7 评论 0原文

我是 Scala 的新手,据我了解,Scala 中的ield 并不像C# 中的yield,它更像是select。

Scala有类似C#的yield的东西吗? C# 的yield 非常好,因为它使编写迭代器变得非常容易。

更新:这是一个来自 C# 的伪代码示例,我希望能够在 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;
      }
   }
}

此代码使用yield 实现图的迭代广度优先遍历,它返回一个迭代器,以便调用者可以使用常规的 for 循环遍历图形,例如:

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

在 C# 中,yield 只是语法糖,可以轻松编写迭代器(.Net 中的 IEnumerable,类似于 Java 中可迭代)。作为一个迭代器,它的计算是惰性的。

更新 II: 我在这里可能是错的,但我认为 C# 中的yield 的全部意义在于让您不必编写更高阶的函数。例如,您可以编写常规 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))。

I'm new to Scala, and from what I understand yield in Scala is not like yield in C#, it is more like select.

Does Scala have something similar to C#'s yield? C#'s yield is great because it makes writing iterators very easy.

Update: here's a pseudo code example from C# I'd like to be able to implement in 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;
      }
   }
}

This code implements an iterative breadth first traversal of a graph, using yield, it returns an iterator, so that callers can traverse the graph using a regular for loop, e.g.:

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

In C#, yield is just syntactic sugar to make it easy to write an iterator (IEnumerable<T> in .Net, similar to Iterable in Java). As an iterator, its evaluated lazily.

Update II: I could be wrong here, but I think the whole point of yield in C# is so that you don't have to write a higher order function. E.g. you can write a regular for loop or use a method like select/map/filter/where instead of passing in a function which will then traverse the sequence.

E.g. graph.iterator().foreach(n => println(n)) instead of graph.iterator( n => println(n)).

This way you can chain them easily, e.g graph.iterator().map(x => x.foo).filter(y => y.bar >= 2).foreach(z => println(z)).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

小霸王臭丫头 2024-08-17 01:27:54

这里对单词“yield”的劫持偏离了它通常的意图:作为 协程。上面示例中的 C# BreadthFirstIterator 似乎在协程意义上使用了 yield;在 yield 返回值后,对活动 BreadthFirstIteratorIEnumerable 的下一次调用将继续执行 yield< 之后的下一条语句/代码>。

在 C# 中,yield 与迭代的思想相结合< /a> 不是一个更通用的控制流语句,但在该有限域内,它的行为是协程的行为。 Scala 的分隔延续可以允许定义协程。在那之前,Scala 缺乏这样的功能,特别是考虑到它的 yield 的替代含义。

The hijacking of the word yield here distracts from its usual intent: as an entry/exit marker in a coroutine. The C# BreadthFirstIterator in the example above appears to use yield in its coroutine sense; after a value is returned by yield, the next call to active BreadthFirstIterator's IEnumerable will continue with the next statement after yield.

In C#, yield is coupled to the idea of iteration rather than being a more general control flow statement, but within that limited domain its behavior is that of a coroutine. Scala's delimited continuations may allow one to define coroutines. Until then, Scala lacks such a capability, especially given its alternate meaning for yield.

半夏半凉 2024-08-17 01:27:54

是的,您可能想看看这个问题的答案:
Scala 的产量是多少?

以下是 Scala 中针对此类构造的文档:
http://www.scala-lang.org/node/111

更新:

此博客讨论了 C# Yield 和 Scala:
http: //hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-1-mixins-and-traits.html

他详细介绍了如何使用扩展来制作与在 Scala 中使用 Traits 相比,IEnumerable 的工作量更大。

所以,你是对的,yield 在 Scala 中的功能与 C# 不同,但那是因为它们非常不同,所以如果你想将 BreadthFirst 作为 Trait 来执行,那么你可以调用 map( )filterforeach 方法,就像在 C# 中一样,但该特征将有助于解决如何遍历集合的问题。

Yes it does, you may want to look at this question for the answer:
What is Scala's yield?

Here is the docs from Scala for this type of construct:
http://www.scala-lang.org/node/111

UPDATE:

This blog talks about C# yield and Scala:
http://hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-1-mixins-and-traits.html

He goes into some detail about how extensions are being used to make IENumerable work compared to using Traits in Scala.

So, you are correct that yield won't function the same way in Scala as C#, but that is because they are very different, and so if you want to do this BreadthFirst as a Trait then you can call the map() and filter and foreach methods, just as you would in C#, but the trait will help solve the problem of how to traverse the collection.

屋顶上的小猫咪 2024-08-17 01:27:54

我认为答案(除非 2.8 中发生变化)答案是否定的,Scala 没有类似于 C# 的yield 的语法糖来编写迭代器(IEumerable 或 Iterable 的实现)。

然而,在 Scala 中,您可以通过向遍历传递一个函数来实现类似的结果,该函数将在遍历中的每个项目上调用该函数。这种方法也可以在 C# 中以相同的方式实现。

以下是我在 C# 中编写 Traverse 的方法,而不使用 Yield:

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);
});

I think the answer (barring changes in 2.8) is that the answer is no, Scala does not have syntactic sugar similar to C#'s yield to write iterators (implementations of IEumerable or Iterable).

However, in Scala you could instead achieve a similar result by passing in a function to the traversal which it would invoke on each item in the traversal. This approach could also be implemented in the same fashion in C#.

Here is how I'd write Traverse in C# without the use of yield:

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;
      }
   }
}

You could then use it like this:

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

Or like this:

graph.BreadthFirstTraversal( n =>
{
   Console.WriteLine(n);
   DoSomeOtherStuff(n);
});
甜点 2024-08-17 01:27:54

尽管 Scala 有一个关键字 yield,但它与 C# yield 有很大不同,Ruby 的 yield 也与两者不同。这似乎是一个被过度使用的关键字。乍一看,yield 在 C# 中的使用似乎非常有限。

要在 Scala 中执行相同的操作,您可以定义自己的高阶函数。在英语中,这意味着一个以函数作为参数的函数。

要采用 Microsoft 的示例,这里有一个 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(_)})

Scala: 1,C#:0


更新:

对于您刚刚添加的示例,编写 traverse 来执行您想要的遍历,而无需考虑如何使用它。然后通过在traverse参数列表后面添加(f(Node) => Any)来添加额外的参数,例如

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

traverse中的点如果您有一个在 C# 中yield 的值,请调用 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)
}

这是“函数式编程”的基本案例,您应该确保理解它才能成功使用 Scala。

Even though Scala has a keyword yield, it's quite different from the C# yield, and Ruby's yield is different from both. It seems to be a wildly overused keyword. The use of yield in C# appears very limited at first glance.

To do the same in Scala, you could define your own high-order function. In English, that means a function that takes a function as a parameter.

To take Microsoft's example, here's a Scala method:

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

Now you have your "iterator":

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

Notes:

  • Powers(2,8) is the same as Powers.apply(2,8). That's just a compiler trick.
  • This method is defined with two parameter lists, which might be confusing. It just allows you to do: Powers(2, 8){ println(_) } instead of Powers(2, 8, {println(_)})

Scala: 1, C#: 0


Update:

For your just-added example, write traverse that does the traversal you want without thinking about how you are going to use it. Then add an extra parameter by adding (f(Node) => Any) after the traverse parameter list, e.g.

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

At the point in traverse where you have a value you would yield with in C#, call f(yieldValue).

When you want to use this "iterator," call traverse and pass a function to it that does whatever it is you want to do for each element in the iterator.

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

This is a basic case for "functional programming" and you should make sure you understand it to be successful with Scala.

温馨耳语 2024-08-17 01:27:54

您可以在 Scala >= 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)
      }
    }
  }
}

You can do this in Scala >= 2.8 using an implementation of generators in terms of delimited continuations. You'll need the continuations plugin and then something along these lines,

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)
      }
    }
  }
}
忆梦 2024-08-17 01:27:54

正如已经提到的,您可以使用 continuations-plugin 创建一个生成器来创建一个yield,其行为与 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( () )
      ()
    }

}

As already mentioned you could create a Generator using the continuations-plugin to create a yield which is exactly behaving like 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( () )
      ()
    }

}
蓝礼 2024-08-17 01:27:54

来自 C# 背景并从 hotzen 调试了 Scala 代码(适应 Scala 2.11.6),我必须说这种延续用法接近于 C#-yield 的等价物。我不知道如果需要多个生成器,以相同的方法运行所有生成器,或者可能分布在不同的方法中,连续性是否仍然会发挥类似的作用,但我很高兴连续性确实存在,这样我就不必被迫使用多个线程来实现类似的,或者传递回调。

Coming from a C# background and having debugged the Scala code from hotzen(adapted to Scala 2.11.6), I must say this continuations usage comes close to the C#-yield equivalent. I do not know if continuations still would function similarly if multiple Generators were needed, running all in the same methods or possibly spread over different methods, but I am happy continuations do exist, so that I am not forced to work with multiple threads to achieve similar, or pass along call-backs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文