Scala:如何避免var并在特殊情况下返回地图

时间:2018-01-19 20:52:15

标签: scala

假设我想编写一个函数foo,它迭代一个列表List[A],并在每个元素上调用一些函数f。该函数返回Option[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)
}

但是这个代码看起来很难看,因为我必须使用varreturn。有没有办法让它看起来更好?

修改

在这个问题中我假设f是一个非常耗时的功能,如果我们已经知道结果应该是None

我想避免调用它

5 个答案:

答案 0 :(得分:3)

可能有几种不同的方法可以做到这一点;这是我能想到的那么简单:

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,你确实会失去短路的好处。您可以通过转换为Stream然后返回来解决这个问题,即val x = list.toStream.map(f)Some(x.flatten.toList)

答案 1 :(得分:1)

这样的事情:

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] }
}

答案 2 :(得分:0)

我会写如下 -

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等,但不是像List这样的集合,那么下面的内容非常简单 -

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

答案 3 :(得分:0)

使用Stream的另一种选择(我认为这是一个很酷的主意)是使用一个允许衬衫电路的显式尾递归:

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

这显然比基于Stream的方法更多的代码,但我希望在某些数据上它可能更快,但可能没有那么快(在所有 - Some好的情况下)与代码一样因map强制执行acc.reverse而导致简单List

答案 4 :(得分:0)

这种事情通常是递归地完成的:

@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写为

case head::tail => f(head).flatMap { foo(tail, _ :: result)(f) }

但是我想,这会杀死尾递归...