этот конфиг работал
./spark-shell --packages com.amazonaws:aws-java-sdk:1.7.4,org.apache.hadoop:hadoop-aws:2.7.3 --conf spark.hadoop.fs.s3a.endpoint=s3.us-east-2.amazonaws.com --conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem --conf spark.hadoop.fs.s3a.aws.credentials.provider=com.amazonaws.auth.InstanceProfileCredentialsProvider --conf spark.executor.extraJavaOptions=-Dcom.amazonaws.services.s3.enableV4=true --conf spark.driver.extraJavaOptions=-Dcom.amazonaws.services.s3.enableV4=true
Я думаю, что ответ (за исключением изменений в 2.8) заключается в том, что ответ отрицательный, в Scala нет синтаксического сахара, аналогичного C # yield для написания итераторов (реализации IEumerable или Iterable).
Однако в Scala вы могли бы вместо этого добиться аналогичного результата, передав функцию обходу, которую она будет вызывать для каждого элемента обхода. Этот подход можно было бы таким же образом реализовать в C #.
Вот как я бы написал Traverse на C # без использования 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);
});
Да, возможно, вы можете посмотреть на этот вопрос, чтобы получить ответ: Каков выход 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
Он углубляется в некоторые детали о том, как используются расширения для обеспечения работы IENumerable по сравнению с использованием черт в Scala.
Итак, вы правы, что yield не будет работать в Scala так же, как C #, но это потому, что они очень разные, и поэтому, если вы хотите сделать BreadthFirst как Trait, вы можете вызвать map()
и filter
и foreach
, как в C #, но эта черта поможет решить проблему обхода коллекции.
Несмотря на то, что у 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
, что делает обход, который вы хотите, не думая о том, как вы собираетесь его использовать. Затем добавьте дополнительный параметр, добавив (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)
}
Это базовый случай «функционального программирования», и вы должны убедиться, что понимаете, что он успешен со Scala.
Исходя из фона C # и отладки кода Scala из hotzen (адаптированного к Scala 2.11.6), я должен сказать, что использование этих продолжений приближается к эквиваленту C # -производства. Я не знаю, будут ли продолжения функционировать аналогично, если бы потребовалось несколько Генераторов, работающих все в одних и тех же методах или, возможно, распределенных по разным методам, но я рад, что продолжения существуют, так что я не вынужден работать с несколькими потоками для достижения похожи или передают обратные вызовы.
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
.
Вы можете сделать это в 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)
}
}
}
}
Как уже упоминалось вы можете создать генератор, используя плагин продолжения, чтобы создать 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( () )
()
}
}