微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将尾递归方法转换为更像 Scala 的函数?

如何解决如何将尾递归方法转换为更像 Scala 的函数?

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

通常,我使用尾递归方法来实现:

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 是合适的替代品吗?我怎样才能让它工作?

解决方法

根据我之前的评论,下面是如何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. 内存效率 - 由于我们正在构建一个 LazyList,并且没有任何东西占据结果列表的头部,因此除了 last 之外的每个元素应该立即释放以进行垃圾收集。
,

您可以像这样使用 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)
    })
  })
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。