很像这个问题:
Functional code for looping with early exit
说代码是
def findFirst[T](objects: List[T]):T = {
for (obj <- objects) {
if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
}
None
}
如何在scala中从for循环中生成单个元素?
我不想使用find,就像原始问题中提出的那样,我很想知道是否以及如何使用for循环实现它。
*更新*
首先,感谢所有评论,但我想我在问题中并不清楚。我正在拍摄这样的事情:
val seven = for {
x <- 1 to 10
if x == 7
} return x
这不会编译。这两个错误是: - 返回外部方法定义 - 方法main有return语句;需要结果类型
我知道find()在这种情况下会更好,我只是在学习和探索语言。在一个更复杂的情况下,有几个迭代器,我认为找到for实际上可以是有用的。
感谢评论者,我会开始赏金以弥补问题的不良后果:)
答案 0 :(得分:21)
如果你想使用for
循环,它使用比.find
,.filter
等链式调用更好的语法,有一个巧妙的技巧。而不是迭代像列表这样的严格集合,而不是像迭代器或流这样的惰性集合。如果你是从一个严格的集合开始,让它变得懒惰,例如.toIterator
。
我们来看一个例子。
首先让我们定义一个“吵闹”的int,它将在调用时显示我们
def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }
现在让我们填写一些列表:
val l = List(1, 2, 3, 4).map(noisyInt)
我们希望找到第一个均匀的元素。
val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v
上述结果导致:
Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)
...意味着访问了所有元素。这是有道理的,因为结果列表包含所有偶数。让我们这次迭代一个迭代器:
val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)
这导致:
Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator
请注意,循环仅在可以判断结果是空的还是非空的迭代器时执行。
要获得第一个结果,您现在只需拨打r2.next
。
如果您想要Option
类型的结果,请使用:
if(r2.hasNext) Some(r2.next) else None
编辑此编码中的第二个示例只是:
val seven = (for {
x <- (1 to 10).toIterator
if x == 7
} yield x).next
...当然,如果您要使用.next
,您应该确保始终至少有一个解决方案。或者,使用为所有headOption
定义的Traversable
来获取Option[Int]
。
答案 1 :(得分:18)
您可以将列表转换为流,以便只有按需评估for循环包含的任何过滤器。但是,从流中产生的总是会返回一个流,而你想要的是我想要一个选项,因此,作为最后一步,你可以检查结果流是否至少有一个元素,并将其头部作为一个选项返回。 headOption函数就是这样做的。
def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
(for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption
答案 2 :(得分:3)
为什么不尽早完成你在上面描绘的内容,即早期的return
?如果您对Scala实际上做了什么感兴趣,请使用-print
运行您的代码。 Scala将循环变为foreach
,然后使用异常提前离开foreach
。
答案 3 :(得分:2)
所以你要做的就是在条件满足后打破一个循环。这里的答案可能就是你要找的东西。 How do I break out of a loop in Scala?
总的来说,Scala中的理解被翻译成map,flatmap和filter操作。因此除非你抛出异常,否则不可能打破这些函数。
答案 4 :(得分:1)
如果您想知道,这就是LineerSeqOptimized.scala中实现查找的方式;哪个List继承
override /*IterableLike*/
def find(p: A => Boolean): Option[A] = {
var these = this
while (!these.isEmpty) {
if (p(these.head)) return Some(these.head)
these = these.tail
}
None
}
答案 5 :(得分:1)
这是一个可怕的黑客。但它会得到你想要的结果。
通过惯用法,您可以使用流或视图来计算所需的部分。
def findFirst[T](objects: List[T]): T = {
def expensiveFunc(o : T) = // unclear what should be returned here
case class MissusedException(val data: T) extends Exception
try {
(for (obj <- objects) {
if (expensiveFunc(obj) != null) throw new MissusedException(obj)
})
objects.head // T must be returned from loop, dummy
} catch {
case MissusedException(obj) => obj
}
}
答案 6 :(得分:1)
为什么不喜欢
object Main {
def main(args: Array[String]): Unit = {
val seven = (for (
x <- 1 to 10
if x == 7
) yield x).headOption
}
}
如果值满足条件
,变量seven
将成为持有Some(value)
的选项
答案 7 :(得分:0)
我希望能帮助你。
我认为......没有'返回'的意思。
object TakeWhileLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else
seq(seq.takeWhile(_ == null).size)
}
object OptionLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T], index: Int = 0): T = if (seq.isEmpty) null.asInstanceOf[T] else
Option(seq(index)) getOrElse func(seq, index + 1)
}
object WhileLoop extends App {
println("first non-null: " + func(Seq(null, null, "x", "y", "z")))
def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else {
var i = 0
def obj = seq(i)
while (obj == null)
i += 1
obj
}
}
答案 8 :(得分:0)
objects iterator filter { obj => (expensiveFunc(obj) != null } next
诀窍是在集合上获得一些惰性评估视图,可以是迭代器或Stream,也可以是objects.view。过滤器只会根据需要执行。