代码之家  ›  专栏  ›  技术社区  ›  waterlooalex

scala的产量是否与c相当?

  •  24
  • waterlooalex  · 技术社区  · 15 年前

    我对scala是个新手,据我所知,scala中的yield与c中的yield不同,它更像是select。

    斯卡拉有类似于C的产量吗?C的收益非常大,因为它使编写迭代器非常容易。

    更新: 下面是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只是语法上的糖分,使编写迭代器变得容易。( 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)) .

    7 回复  |  直到 8 年前
        1
  •  10
  •   seh Alexei    15 年前

    劫持这个词 产量 这里分散了它通常的意图:作为一个入口/出口标记 coroutine . C语言 BreadthFirstIterator 在上面的示例中,似乎使用 yield 从协程的意义上讲;在返回值之后 产量 ,下一个激活的调用 breadthfirst迭代器 IEnumerable 将继续下一个声明之后 产量 .

    在C, 产量 coupled to the idea of iteration 而不是更一般的控制流语句,但在这个有限的域中,它的行为是协同程序。斯卡拉 分隔的延续 允许定义协程。在此之前,scala缺乏这样的能力,特别是考虑到它对 产量 .

        2
  •  4
  •   Community CDub    7 年前

    是的,你可能想看看这个问题的答案: What is Scala's yield?

    以下是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

    他详细介绍了与在scala中使用特性相比,如何使用扩展来实现IEnumerable工作。

    所以,你是正确的,产量在scala中不会像c那样起作用,但那是因为它们是非常不同的,所以如果你想先做这个试验作为一个特性,那么你可以称之为 map() filter foreach 方法,就像在C中一样,但是特性将帮助解决如何遍历集合的问题。

        3
  •  4
  •   Lii bob    8 年前

    我认为答案(除了2.8中的变化)是否定的,scala没有类似于c编写迭代器(ienumerable或iterable的实现)的语法糖。

    但是,在scala中,您可以通过将一个函数传递给遍历来实现类似的结果,它将对遍历中的每个项调用该函数。这种方法也可以用同样的方式在C中实现。

    以下是我如何在不使用yield的情况下用c编写travel:

    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);
    });
    
        4
  •  3
  •   Alex Neth    15 年前

    尽管scala有一个关键字 yield 它和C很不一样# 产量 和露比的 产量 两者都不同。这似乎是一个过度使用的关键词。使用 产量 在C中,乍一看似乎非常有限。

    要在scala中做同样的事情,您可以定义自己的高阶函数。在英语中,这意味着一个以函数为参数的函数。

    采取 Microsoft's example ,下面是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) 导线 参数列表,例如

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

    在这一点上 横移 如果你有价值的话 产量 在C中,呼叫 f(yieldValue) .

    如果要使用此“迭代器”,请调用 导线 并向它传递一个函数,它可以为迭代器中的每个元素做任何您想做的事情。

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

    这是“函数式编程”的一个基本情况,您应该确保理解它,以便在scala中获得成功。

        5
  •  3
  •   Miles Sabin    15 年前

    您可以在scala>=2.8中使用生成器的分隔延续实现来完成此操作。你需要 continuations plugin 然后沿着这些线,

    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)
          }
        }
      }
    }
    
        6
  •  2
  •   hotzen    15 年前

    如前所述,您可以使用Continuations插件创建一个生成器,以创建一个行为与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( () )
          ()
        }
    
    }
    
        7
  •  0
  •   wolfgang grinfeld    10 年前

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