如何将尾递归方法转换为更像 Scala 的函数?
How to convert tail recursive method to more Scala-like function?
在我的代码中,我经常需要通过对内部模型执行操作来处理列表。对于每个处理过的元素,都会返回模型,然后 'new' 模型用于列表的下一个元素。
一般我都是用尾递归的方式实现的:
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
是合适的替代品吗?我怎样才能让它工作?
您可以像这样使用 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)
})
})
}
根据我之前的评论,以下是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
这里的好处是:
- 提前终止 - 在第一个
Left
返回后或在处理完所有记录后,迭代 records
停止,以先到者为准。
- 高效内存 - 由于我们正在构建一个
LazyList
,并且结果列表的头部没有任何内容,除了 [=15] =] 应该立即释放以进行垃圾收集。
在我的代码中,我经常需要通过对内部模型执行操作来处理列表。对于每个处理过的元素,都会返回模型,然后 'new' 模型用于列表的下一个元素。
一般我都是用尾递归的方式实现的:
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
是合适的替代品吗?我怎样才能让它工作?
您可以像这样使用 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)
})
})
}
根据我之前的评论,以下是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
这里的好处是:
- 提前终止 - 在第一个
Left
返回后或在处理完所有记录后,迭代records
停止,以先到者为准。 - 高效内存 - 由于我们正在构建一个
LazyList
,并且结果列表的头部没有任何内容,除了 [=15] =] 应该立即释放以进行垃圾收集。