Scala:如何避免特殊情况下映射中的var和return
假设我想编写一个函数Scala:如何避免特殊情况下映射中的var和return,scala,Scala,假设我想编写一个函数foo,它在一个列表list[a]上迭代,并在每个元素上调用一些函数f。此函数返回选项[B]。如果遇到None,我希望整个函数foo返回None。如果没有,我希望它返回整个结果列表list[B] 我可以这样写: def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = { var res: List[B] = Nil for (element <- list) {
foo
,它在一个列表list[a]
上迭代,并在每个元素上调用一些函数f
。此函数返回选项[B]
。如果遇到None
,我希望整个函数foo
返回None
。如果没有,我希望它返回整个结果列表list[B]
我可以这样写:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
var res: List[B] = Nil
for (element <- list) {
f(element) match {
case Some(fRes) => res = fRes :: res
case _ => return None
}
}
Some(res.reverse)
}
def foo[A, B](list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val lst = list.view.map(f)
if(lst.exists(_.isEmpty)) None
else Option { lst.flatten.to[List] }
}
def foo(list:list[A],f:(A)=>Option[B]):Option[list[B]={
var res:List[B]=Nil
对于(元素res=fRes::res)
case=>返回None
}
}
一些(反向)
}
但是这段代码看起来很难看,因为我必须使用var
和return
。有没有办法让它看起来更好
编辑
在这个问题中,我假设
f
是一个非常耗时的函数,如果我们已经知道结果应该是None
的话,我想避免调用它。可能有几种不同的方法可以做到这一点;下面是我能想到的最简单的方法:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val x = list.map(f)
if (x.contains(None)) {
None
} else {
Some(x.flatten)
}
}
如果遇到
None
,您确实会失去短路的好处。您可以通过将流转换为并返回,即val x=list.toStream.map(f)
和Some(x.flatte.toList)来解决这一问题
可能有几种不同的方法可以做到这一点;以下是我能想到的最简单的方法:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val x = list.map(f)
if (x.contains(None)) {
None
} else {
Some(x.flatten)
}
}
如果遇到无
,您确实会失去短路的好处。您可以通过将流转换为流
,然后再转换回来,即val x=list.toStream.map(f)
和Some(x.flatte.toList)
类似以下内容:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
var res: List[B] = Nil
for (element <- list) {
f(element) match {
case Some(fRes) => res = fRes :: res
case _ => return None
}
}
Some(res.reverse)
}
def foo[A, B](list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val lst = list.view.map(f)
if(lst.exists(_.isEmpty)) None
else Option { lst.flatten.to[List] }
}
大概是这样的:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
var res: List[B] = Nil
for (element <- list) {
f(element) match {
case Some(fRes) => res = fRes :: res
case _ => return None
}
}
Some(res.reverse)
}
def foo[A, B](list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val lst = list.view.map(f)
if(lst.exists(_.isEmpty)) None
else Option { lst.flatten.to[List] }
}
我会这样写:
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val newList = list.map(x => f(x))
if(newList.contains(None)) None else Some(newList.flatten)
}
如果B是简单类型,如Int、Double等,但不是集合类型的列表,那么下面的代码将非常简单-
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val newList = list.flatMap(f)
if(newList.size < list.size) None else Some(newList)
}
def foo(list:list[A],f:(A)=>Option[B]):Option[list[B]={
val newList=list.flatMap(f)
如果(newList.size
我将写如下-
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val newList = list.map(x => f(x))
if(newList.contains(None)) None else Some(newList.flatten)
}
如果B是简单类型,如Int、Double等,但不是集合类型的列表,那么下面的代码将非常简单-
def foo(list: List[A], f: (A) => Option[B]): Option[List[B]] = {
val newList = list.flatMap(f)
if(newList.size < list.size) None else Some(newList)
}
def foo(list:list[A],f:(A)=>Option[B]):Option[list[B]={
val newList=list.flatMap(f)
如果(newList.size
使用流的另一种替代方法(我认为这是一个很酷的想法)是使用一个显式尾部递归,它允许:
def foo[A, B](list: List[A])(f: (A) => Option[B]): Option[List[B]] = {
@tailrec
def impl(list: List[A], acc: List[B]): Option[List[B]] = list match {
case a :: rest => f(a) match {
case Some(b) => impl(rest, b :: acc)
case None => None
}
case _ => Some(acc.reverse)
}
impl(list, List.empty)
}
这显然比基于流的方法的代码要多,但我预计在某些数据上,它可能更快,但可能没有那么快(在所有的情况下,有些是好的)由于List
强制执行acc.reverse
,因此使用简单map
的代码使用流
(我认为这是一个很酷的想法)的另一种替代方法是使用显式尾部递归,允许:
def foo[A, B](list: List[A])(f: (A) => Option[B]): Option[List[B]] = {
@tailrec
def impl(list: List[A], acc: List[B]): Option[List[B]] = list match {
case a :: rest => f(a) match {
case Some(b) => impl(rest, b :: acc)
case None => None
}
case _ => Some(acc.reverse)
}
impl(list, List.empty)
}
这显然比基于流的方法的代码要多,但我预计在某些数据上,它可能更快,但可能没有那么快(在所有的情况下,有些是好的)由于List
强制执行acc.reverse
,因此使用简单map
的代码通常递归执行此类操作:
@tailrec
def foo(
list: List[A],
result: List[B] = Nil
)(f: A => Option[B]): Option[List[B]] = list match {
case Nil => Some(result.reverse)
case head :: tail => f(head) match {
case Some(b) => foo(tail, b::result)(f)
case None => None
}
}
您还可以将最后一个案例写为
case head::tail => f(head).flatMap { foo(tail, _ :: result)(f) }
但我认为,这会扼杀尾部递归…这类事情通常是递归完成的:
@tailrec
def foo(
list: List[A],
result: List[B] = Nil
)(f: A => Option[B]): Option[List[B]] = list match {
case Nil => Some(result.reverse)
case head :: tail => f(head) match {
case Some(b) => foo(tail, b::result)(f)
case None => None
}
}
您还可以将最后一个案例写为
case head::tail => f(head).flatMap { foo(tail, _ :: result)(f) }
但是我认为,这会杀死尾部递归…是的,对我来说,map
的问题是有时f
是一个非常耗时的函数。不过,我没有想到流,谢谢,对我来说,map
的问题是有时候f
是一个非常耗时的函数。我没有想到thanksCool,我不知道Scala中的视图,但看起来更像Joe K的回答。酷,我不知道Scala中的视图,但看起来更像Joe K的回答