2

在我的代码中,我经常需要通过对内部模型执行操作来处理列表。对于每个处理过的元素,返回模型,然后将“新”模型用于列表的下一个元素。

通常,我通过使用尾递归方法来实现这一点:

def createCar(myModel: Model, record: Record[Any]): Either[CarError, Model] = {
  record match {
    case c: Car =>
      // Do car stuff...
      val newModel: Model = myModel.createCar(record)
      Right(newModel)
    case _ => Left(CarError())
  }
}

@tailrec
def processCars(myModel: Model, records: List[Record[Any]]): Either[CarError, Model] =
  records match {
    case x :: xs =>
      createCar(myModel, x) match {
        case Right(m) => processCars(m, xs)
        case e@Left(_) => e
      }
    case Nil => Right(myModel)
  }

由于我不断重复这种模式,我正在寻找使其更简洁和更实用的方法(即 Scala 方式)。我已经调查过foldLeft,但无法使用Either

recordsList.foldLeft(myModel) { (m, r) =>
      // Do car stuff...           
      Right(m)
}

foldLeft合适的替代品吗?我怎样才能让它工作?

4

2 回答 2

4

跟进我之前的评论,这是unfold()获得结果的方法。[注:Scala 2.13.x]

def processCars(myModel: Model
               ,records: List[Record[_]]
               ): Either[CarError, Model] =
  LazyList.unfold((myModel,records)) { case (mdl,recs) =>
    recs.headOption.map{
      createCar(mdl, _).fold(Left(_) -> (mdl,Nil)
                            ,m => Right(m) -> (m,recs.tail))
    }
  }.last

这里的优点是:

  1. 提前终止records-在第一个Left返回后或在所有记录都已处理后迭代停止,以先到者为准。
  2. 内存效率- 由于我们正在构建 a LazyList,并且没有任何东西保留在结果列表的头部,因此除了 之外的每个元素都last 应该立即释放以进行垃圾收集。
于 2021-02-19T01:12:10.013 回答
-1

你可以这样做fold

def processCars(myModel: Model, records: List[Record[Any]]): Either[CarError, Model] = {
  records.foldLeft[Either[CarError, Model]](Right(myModel))((m, r) => {
    m.fold(Left.apply, { model =>
      createCar(model, r).fold(Left.apply, Right.apply)
    })
  })
}
于 2021-02-18T20:39:10.407 回答