Scala打破集合的惯用方式是什么;s`find`方法在出现错误时失败?
打破find循环并将其映射到失败的惯用方法是什么Scala打破集合的惯用方式是什么;s`find`方法在出现错误时失败?,scala,collections,Scala,Collections,打破find循环并将其映射到失败的惯用方法是什么 我希望避免使用异常(抛出异常并将其包装到Try()中) 我想避免使用return 这应该或多或少有性能(因此我相信递归不会工作,也不会折叠,因为它会将集合扫描到底) 也许吧 def findUseById(userId: Long, usersJson: Seq[JsObject]): Try[Option[JsObject]] = { usersJson.find { userJson => (userJson \ "id"
- 我希望避免使用异常(抛出异常并将其包装到Try()中)
- 我想避免使用
return
- 这应该或多或少有性能(因此我相信递归不会工作,也不会折叠,因为它会将集合扫描到底) 也许吧
def findUseById(userId: Long, usersJson: Seq[JsObject]): Try[Option[JsObject]] = {
usersJson.find { userJson =>
(userJson \ "id").validate[Long] {
case Right(uid) => uid == userId
case Left(error) =>
// How to break find loop and "map" it to failure
// (like Failure(new SomeException())) ?
}
}
}
尽管这违反了您的第一个要求。这里有一个简单、高效且功能强大的问题解决方案。
它利用了迭代器的惰性和
collectFirst
提前返回的优势
但是,我不确定选项的尝试是否是最好的类型。例如,您可以只删除结尾处的匹配项,并返回一个Try的选项,或者只对找不到的值使用自定义异常
import scala.util.{Failure, Success, Try}
def validateAndFind[A, B](data: Seq[A], target: B)
(validationFun: A => Either[Throwable, (A, B)]): Try[Option[A]] =
data
.iterator
.map(validationFun)
.collectFirst {
case Right((elem, value)) if (value == target) => Right(elem)
case Left(ex) => Left(ex)
} match {
case None => Success(None)
case Some(Right(elem)) => Success(Some(elem))
case Some(Left(ex)) => Failure(ex)
}
无论如何,这两种代码都是等效的,并按预期工作:
sealed trait ValidationError extends Product with Serializable
final case object ElementNotFound extends ValidationError
final case class ValidationFailure(cause: Throwable) extends ValidationError
def validateAndFind[A, B](data: Seq[A], target: B)
(validationFun: A => Either[Throwable, (A, B)]): Either[ValidationError, A] =
data
.iterator
.map(validationFun)
.collectFirst {
case Right((elem, value)) if (value == target) => Right(elem)
case Left(ex) => Left(ValidationFailure(cause = ex))
}.getOrElse(Left(ElementNotFound))
最终案例类用户(名称:String,年龄:Int)
验证查找(
数据=列表(
用户(name=“Balmung”,年龄=22岁),
用户(name=“Luis”,年龄=22岁),
用户(name=“Miguel”,年龄=22岁)
),
target=“路易斯”
){user=>
println(s“验证用户:${user}”)
如果(user.age<18)离开(新的IllegalArgumentException(“未成年用户”))
else权限(用户->用户名)
}
//验证用户:用户(Balmung,22岁)
//验证用户:用户(Luis,22)
//res:not[ValidationError,User]=右(User(“Luis”,22))
如您所见,它没有验证第三个元素,因为它不是必需的。让我们看看另外两个要求
final case class User(name: String, age: Int)
validateAndFind(
data = List(
User(name = "Balmung", age = 22),
User(name = "Luis", age = 22),
User(name = "Miguel", age = 22)
),
target = "Luis"
) { user =>
println(s"Validating user: ${user}")
if (user.age < 18) Left(new IllegalArgumentException("User underage"))
else Right(user -> user.name)
}
// Validating user: User(Balmung,22)
// Validating user: User(Luis,22)
// res: Either[ValidationError, User] = Right(User("Luis", 22))
validateAndFind(
数据=列表(
用户(name=“Balmung”,年龄=16岁),
用户(name=“Luis”,年龄=22岁),
用户(name=“Miguel”,年龄=22岁)
),
target=“路易斯”
){user=>
println(s“验证用户:${user}”)
如果(user.age<18)离开(新的IllegalArgumentException(“未成年用户”))
else权限(用户->用户名)
}
//验证用户:用户(Balmung,16岁)
//res:not[ValidationError,User]=左(ValidationFailure(java.lang.IllegalArgumentException:未成年用户))
及
validateAndFind(
数据=列表(
用户(name=“Balmung”,年龄=22岁),
用户(name=“Luis”,年龄=22岁),
用户(name=“Miguel”,年龄=22岁)
),
target=“马里奥”
){user=>
println(s“验证用户:${user}”)
如果(user.age<18)离开(新的IllegalArgumentException(“未成年用户”))
else权限(用户->用户名)
}
//验证用户:用户(Balmung,22岁)
//验证用户:用户(Luis,22)
//验证用户:用户(Miguel,22岁)
//res:not[ValidationError,User]=左(ElementNotFound)
您可以使用return
@jrookreturn
提前返回在函数式编程中有点不惯用至少在Scala 2.13集合中,find
方法使用return
(除了java风格while
循环之外)。我想不出比立即从函数返回并继续执行程序其余部分更有效的方法了。@jrook Scala的集合实现本身不是惯用的,因为这些函数概念最终应该以某种方式映射到JVM。。它们为我们提供了编写惯用代码的api。如果您在列表的第三个位置找到了值,但第五个项目没有验证,那么预期的行为是什么?它应该返回成功还是验证错误?我认为.map(\u==userId)
应该是.map(\ucode.get==userId)
@jrook您可能是对的。由于我不确定使用的是什么库,所以我假设validateOpt
返回一个类似于播放json的选项
final case class User(name: String, age: Int)
validateAndFind(
data = List(
User(name = "Balmung", age = 22),
User(name = "Luis", age = 22),
User(name = "Miguel", age = 22)
),
target = "Luis"
) { user =>
println(s"Validating user: ${user}")
if (user.age < 18) Left(new IllegalArgumentException("User underage"))
else Right(user -> user.name)
}
// Validating user: User(Balmung,22)
// Validating user: User(Luis,22)
// res: Either[ValidationError, User] = Right(User("Luis", 22))
validateAndFind(
data = List(
User(name = "Balmung", age = 16),
User(name = "Luis", age = 22),
User(name = "Miguel", age = 22)
),
target = "Luis"
) { user =>
println(s"Validating user: ${user}")
if (user.age < 18) Left(new IllegalArgumentException("User underage"))
else Right(user -> user.name)
}
// Validating user: User(Balmung,16)
// res: Either[ValidationError, User] = Left(ValidationFailure(java.lang.IllegalArgumentException: User underage))
validateAndFind(
data = List(
User(name = "Balmung", age = 22),
User(name = "Luis", age = 22),
User(name = "Miguel", age = 22)
),
target = "Mario"
) { user =>
println(s"Validating user: ${user}")
if (user.age < 18) Left(new IllegalArgumentException("User underage"))
else Right(user -> user.name)
}
// Validating user: User(Balmung,22)
// Validating user: User(Luis,22)
// Validating user: User(Miguel,22)
// res: Either[ValidationError, User] = Left(ElementNotFound)