我正在编写一个Scala程序,其中有一个创建序列的操作。操作可能会失败,因此我将其括在Try
内。我想在for comprehension中进行序列创建和枚举,以便成功创建的序列产生一系列元组,其中第一个元素是序列,第二个元素是元素。
要简化问题,请将我的序列设为Range
整数,并定义createRange
函数,如果要求它创建一个奇数长度的范围,则该函数会失败。这是一个简单的理解,可以做我想要的。
import scala.util.Try
def createRange(n: Int): Try[Range] = {
Try {
if (n % 2 == 1) throw new Exception
else Range(0, n)
}
}
def rangeElements(n: Int) {
for {
r <- createRange(n)
x <- r
} println(s"$r\t$x")
}
def main(args: Array[String]) {
println("Range length 3")
rangeElements(3)
println("Range length 4")
rangeElements(4)
}
如果你运行它,它会正确打印。
Range length 3
Range length 4
Range(0, 1, 2, 3) 0
Range(0, 1, 2, 3) 1
Range(0, 1, 2, 3) 2
Range(0, 1, 2, 3) 3
现在我想重写我的rangeElements
函数,以便它不是作为副作用打印而是返回一个整数序列,如果未创建范围,则序列为空。我想写的就是这个。
def rangeElements(n: Int):Seq[(Range,Int)] = {
for {
r <- createRange(n)
x <- r
} yield (r, x)
}
// rangeElements(3) returns an empty sequence
// rangeElements(4) returns the sequence (Range(0,1,2,3), 0), (Range(0,1,2,3), 1) etc.
这给了我两个类型不匹配的编译器错误。 r <- createRange(n)
行需要Seq[Int]
,但找到了scala.util.Try[Nothing]
。 x <- r
行需要scala.util.Try[?]
,但找到了scala.collection.immutable.IndexedSeq[Int]
。
据推测,Try
有一些类型的擦除让我搞砸了,但我无法弄清楚它是什么。我已经尝试了各种toOption
和toSeq
限定符,以便在理解中无济于事。
如果我只需要产生范围元素,我可以明确处理Success
Failure
我自己的createRange
条件,如下面的前两个答案所示。但是,我需要访问范围及其各个元素。
我意识到这是一个听起来很奇怪的例子。我试图解决的真正问题是一个复杂的递归搜索,但我不想添加所有细节,因为这只会混淆这里的问题。
如何编写rangeElements
以产生所需的序列?
答案 0 :(得分:3)
如果将for comprehension转换为map / flatMap实现(如Scala Language Spec 6.19中所述),问题就会变得清晰。 flatMap的结果类型为Try[U]
,但您的函数需要Seq[Int]
。
for {
r <- createRange(n)
x <- r
} yield x
createRange(n).flatMap {
case r => r.map {
case x => x
}
}
你有没有理由不使用getOrElse
方法?
def rangeElements(n: Int):Seq[Int] =
createRange(n) getOrElse Seq.empty
答案 1 :(得分:1)
当Try
为偶数时Success
为Range
,n
为Failure
,Exception
为n
rangeElements
是奇怪的。在Success
匹配并提取这些值。 Range
将包含有效Failure
,Exception
将包含Exception
。而不是返回Seq
返回空import scala.util.{Try, Success, Failure}
def createRange(n: Int): Try[Range] = {
Try {
if (n % 2 == 1) throw new Exception
else Range(0, n)
}
}
def rangeElements(n: Int):Seq[Tuple2[Range, Int]] = createRange(n) match {
case Success(s) => s.map(xs => (s, xs))
case Failure(f) => Seq()
}
scala> rangeElements(3)
res35: Seq[(Range, Int)] = List()
scala> rangeElements(4)
res36: Seq[(Range, Int)] = Vector((Range(0, 1, 2, 3),0), (Range(0, 1, 2, 3),1), (Range(0, 1, 2, 3),2), (Range(0, 1, 2,3),3))
。
{{1}}