gpt4 book ai didi

验证与析取

转载 作者:行者123 更新时间:2023-12-03 12:58:08 24 4
gpt4 key购买 nike

假设我想编写一个具有以下签名的方法:

def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]]

对于输入中的每一对字符串,它需要验证两个成员都可以解析为整数,并且第一个小于第二个。然后它需要返回整数,累积出现的任何错误。

首先,我将定义一个错误类型:
import scalaz._, Scalaz._

case class InvalidSizes(x: Int, y: Int) extends Exception(
s"Error: $x is not smaller than $y!"
)

现在我可以按如下方式实现我的方法:
def checkParses(p: (String, String)):
ValidationNel[NumberFormatException, (Int, Int)] =
p.bitraverse[
({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
](
_.parseInt.toValidationNel,
_.parseInt.toValidationNel
)

def checkValues(p: (Int, Int)): Validation[InvalidSizes, (Int, Int)] =
if (p._1 >= p._2) InvalidSizes(p._1, p._2).failure else p.success

def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
checkParses(p).fold(_.failure, checkValues _ andThen (_.toValidationNel))
)

或者,或者:
def checkParses(p: (String, String)):
NonEmptyList[NumberFormatException] \/ (Int, Int) =
p.bitraverse[
({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
](
_.parseInt.toValidationNel,
_.parseInt.toValidationNel
).disjunction

def checkValues(p: (Int, Int)): InvalidSizes \/ (Int, Int) =
(p._1 >= p._2) either InvalidSizes(p._1, p._2) or p

def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
checkParses(p).flatMap(s => checkValues(s).leftMap(_.wrapNel)).validation
)

现在无论出于何种原因,第一个操作(验证对解析为字符串)对我来说就像一个验证问题,而第二个操作(检查值)感觉就像一个析取问题,感觉我需要组合这两个单子(monad)(这表明我应该使用 \/ ,因为 ValidationNel[Throwable, _] 没有单子(monad)实例)。

在我的第一个实现中,我使用 ValidationNel从始至终,然后 fold最后作为一种假 flatMap .第二,我在 ValidationNel之间来回跳动。和 \/视情况而定,具体取决于我是否需要错误累积或一元绑定(bind)。它们产生相同的结果。

我已经在实际代码中使用了这两种方法,但还没有形成对其中一种的偏好。我错过了什么吗?我应该更喜欢一个吗?

最佳答案

以下是我对 Cats 的第二版代码的非常接近的翻译。 :

import scala.util.Try

case class InvalidSizes(x: Int, y: Int) extends Exception(
s"Error: $x is not smaller than $y!"
)

def parseInt(input: String): Either[Throwable, Int] = Try(input.toInt).toEither

def checkValues(p: (Int, Int)): Either[InvalidSizes, (Int, Int)] =
if (p._1 >= p._2) Left(InvalidSizes(p._1, p._2)) else Right(p)

import cats.data.{EitherNel, ValidatedNel}
import cats.instances.either._
import cats.instances.list._
import cats.syntax.apply._
import cats.syntax.either._
import cats.syntax.traverse._

def checkParses(p: (String, String)): EitherNel[Throwable, (Int, Int)] =
(parseInt(p._1).toValidatedNel, parseInt(p._2).toValidatedNel).tupled.toEither

def parse(input: List[(String, String)]): ValidatedNel[Throwable, List[(Int, Int)]] =
input.traverse(fields =>
checkParses(fields).flatMap(s => checkValues(s).toEitherNel).toValidated
)

为了更新问题,这段代码是“根据我是否需要错误累积或单子(monad)绑定(bind),在 ValidatedNelEither 之间来回切换”。

在我提出这个问题后的近六年里,Cats 有 introduced a Parallel type class (在 Cats 2.0.0 中改进)正好解决了我遇到的问题:
import cats.data.EitherNel
import cats.instances.either._
import cats.instances.list._
import cats.instances.parallel._
import cats.syntax.either._
import cats.syntax.parallel._

def checkParses(p: (String, String)): EitherNel[Throwable, (Int, Int)] =
(parseInt(p._1).toEitherNel, parseInt(p._2).toEitherNel).parTupled

def parse(input: List[(String, String)]): EitherNel[Throwable, List[(Int, Int)]] =
input.parTraverse(fields =>
checkParses(fields).flatMap(checkValues(_).toEitherNel)
)

我们可以切换 par我们的应用运算符版本,如 traversetupled当我们想要累积错误时,否则我们正在 Either 中工作,这给了我们一元绑定(bind),我们不再需要引用 Validated一点也不。

关于验证与析取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20065853/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com