24

私はScalaを初めて使用します。私が理解していることから、ScalaのyieldはC#のyieldとは異なり、selectに似ています。

ScalaにはC#の歩留まりに似たものがありますか?C#の歩留まりは、イテレータの記述を非常に簡単にするため、優れています。

更新: Scalaで実装できるようにしたい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;
      }
   }
}

このコードは、yieldを使用してグラフの反復幅優先走査を実装し、反復子を返すため、呼び出し元は通常のforループを使用してグラフを走査できます。

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

C#では、yieldはイテレータを簡単に記述できるようにするための単なる構文糖衣です(IEnumerable<T>.NetではIterable、Javaと同様)。イテレータとして、それは怠惰に評価されました。

更新II:ここでは間違っている可能性がありますが、C#でのyieldの全体的なポイントは、高階関数を作成する必要がないようにすることだと思います。たとえば、シーケンスをトラバースする関数を渡す代わりに、通常のforループを記述したり、///のようなメソッドを使用したりできますselectmapfilterwhere

たとえば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))

4

7 に答える 7

10

ここでのyieldという単語のハイジャックは、コルーチンの入口/出口マーカーとしての通常の意図から注意をそらします。上記の例のC#は、コルーチンの意味でBreadthFirstIterator使用されているように見えます。yieldによって値が返された後、アクティブな'syieldへの次の呼び出しは、 。の後の次のステートメントに進みます。BreadthFirstIteratorIEnumerableyield

C#では、より一般的な制御フローステートメントではなく、反復の概念yieldと結びついていますが、その限られたドメイン内では、その動作はコルーチンの動作です。Scalaの区切られた継続により、コルーチンを定義できる場合があります。それまでは、Scalaにはそのような機能がなく、特に。の代替の意味があります。yield

于 2009-11-10T04:47:29.170 に答える
4

はい、そうです。答えについては、この質問を参照してください 。Scalaの歩留まりはどれくらいですか。

このタイプの構成に関する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でTraitsを使用する場合と比較して、IENumerableを機能させるために拡張機能がどのように使用されているかについて詳しく説明します。

つまり、ScalaではyieldがC#と同じように機能しないというのは正しいですが、それは非常に異なるためです。したがって、この幅優先を特性として実行する場合は、andメソッドとメソッドを呼び出すことがmap()できますfilterforeachC#の場合と同じですが、この特性は、コレクションをトラバースする方法の問題を解決するのに役立ちます。

于 2009-10-31T23:02:20.837 に答える
4

答えは(2.8での変更を除いて)答えはノーだと思います。Scalaにはイテレーター(IEumerableまたはIterableの実装)を書くためのC#のyieldに似た構文糖衣がありません。

ただし、Scalaでは、代わりに、トラバーサルの各アイテムで呼び出す関数をトラバーサルに渡すことで、同様の結果を得ることができます。このアプローチは、C#でも同じ方法で実装できます。

これが、yieldを使用せずにC#でTraverseを作成する方法です。

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);
});
于 2009-11-06T03:14:02.027 に答える
3

Scalaにはキーワードがありますがyield、C#とはかなり異なりyield、Rubyyieldは両方とも異なります。酷使されているキーワードのようです。C#での使用はyield、一見非常に制限されているように見えます。

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)です。これは単なるコンパイラのトリックです。
  • このメソッドは2つのパラメーターリストで定義されているため、混乱する可能性があります。それはあなたがすることを可能にするだけです: Powers(2, 8){ println(_) }代わりにPowers(2, 8, {println(_)})

Scala:1、C#:0


アップデート:

追加したばかりの例でtraverseは、どのように使用するかを考えずに、必要なトラバーサルを実行するように記述します。次に、パラメータリスト(f(Node) => Any)の後に追加してパラメータを追加します。traverse

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

C#で使用traverseする値がある時点で、を呼び出します。yieldf(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で成功するためにはそれを理解していることを確認する必要があります。

于 2009-11-05T05:30:49.120 に答える
3

これは、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)
      }
    }
  }
}
于 2010-02-04T20:13:56.383 に答える
2

すでに述べたように、continuations-pluginを使用してジェネレーターを作成し、C#のように正確に動作するyieldを作成できます。

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

}
于 2010-04-16T18:47:23.233 に答える
0

C#のバックグラウンドから来て、hotzen(Scala 2.11.6に適合)からScalaコードをデバッグしたので、この継続の使用法はC#の同等の利回りに近いと言わなければなりません。複数のジェネレーターが必要な場合、すべて同じメソッドで実行されている場合、または異なるメソッドに分散している場合でも、継続が同様に機能するかどうかはわかりませんが、継続が存在することを嬉しく思います。そのため、複数のスレッドで作業して達成する必要はありません。同様、またはコールバックを渡します。

于 2015-04-12T10:07:14.987 に答える