如果一条记录没有相邻的记录,我想登录。有没有纯粹的功能性方法可以做到这一点?可以将副作用与数据转换区分开来的吗?
这是我需要做的一个例子:
val records: Seq[Record] = Seq(record1, record2, ...)
val accountsMap: Map[Long, Account] = Map(record1.id -> account1, ...)
def withAccount(accountsMap: Map[Long, Account])(r: Record): (Record, Option[Account]) = {
(r, accountsMap.get(r.id))
}
def handleNoAccounts(tuple: (Record, Option[Account]) = {
val (r, a) = tuple
if (a.isEmpty) logger.error(s"no account for ${record.id}")
tuple
}
def toRichAccount(tuple: (Record, Option[Account]) = {
val (r, a) = tuple
a.map(acct => RichAccount(r, acct))
}
records
.map(withAccount(accountsMap))
.map(handleNoAccounts) // if no account is found, log
.flatMap(toRichAccount)
因此,我认为这种方法存在多个问题,使其无法达到最佳效果。
元组返回类型很笨拙。我必须在后两个函数中都破坏元组。
日志记录功能必须处理日志记录,然后返回没有任何更改的元组。即使没有进行任何转换,也将其传递给.map感觉很奇怪-也许有更好的方法来获得这种副作用。
是否有功能性的方法可以解决此问题?
答案 0 :(得分:3)
如果您使用的是Scala 2.13或更高版本,则可以使用tapEach,它使用函数A => Unit
在函数的每个元素上施加副作用,然后不变地传递集合:
//you no longer need to return tuple in side-effecting function
def handleNoAccounts(tuple: (Record, Option[Account]): Unit = {
val (r, a) = tuple
if (a.isEmpty) logger.error(s"no account for ${record.id}")
}
records
.map(withAccount(accountsMap))
.tapEach(handleNoAccounts) // if no account is found, log
.flatMap(toRichAccount)
如果您使用的是旧版Scala,则可以提供扩展方法(根据Levi's Ramsey的建议进行了更新):
implicit class SeqOps[A](s: Seq[A]) {
def tapEach(f: A => Unit): Seq[A] = {
s.foreach(f)
s
}
}
答案 1 :(得分:3)
我可能是错的(我经常是),但是我认为这可以满足所有要求。
records
.flatMap(r =>
accountsMap.get(r.id).fold{
logger.error(s"no account for ${r.id}")
Option.empty[RichAccount]
}{a => Some(RichAccount(r,a))})