假设我需要检查给定的数字列表是否以一个或多个1
,一个或多个2
以及一个或多个3
开头。如果检查失败,我希望累积所有错误,例如
val check: List[Int] => Either[String, Unit] = ???
check(Nil) // error : "expected 1", "expected 2", "expected 3"
check(List(1, 1, 3)) // error : "expected 2"
check(List(1, 1, 4)) // errors: "expected 2", "expected 3"
check(List(3, 4, 5)) // errors: "expected 1", "expected 2"
check(List(0, 0, 0)) // errors: "expected 1", "expected 2", "expected 3"
为了实施check
,我正在撰写one
类型的函数two
,three
和List[Int] => Either[String, List[Int]]
:
import cats._, cats.data._, cats.implicits._
def num(n: Int): List[Int] => Either[String, List[Int]] = _ match {
case x::xs => if (x == n) (xs dropWhile (_ == n)).asRight else s"expected $n".asLeft
case _ => s"expected $n".asLeft
}
val one = num(1)
val two = num(2)
val three = num(3)
scala> one(Nil)
res70: Either[String,List[Int]] = Left(expected 1)
scala> one(List(1, 1, 1))
res71: Either[String,List[Int]] = Right(List())
scala> one(List(2, 1, 1, 1))
res72: Either[String,List[Int]] = Left(expected 1)
scala> one(List(2, 3, 1, 1, 1))
res73: Either[String,List[Int]] = Left(expected 1)
如何撰写函数one
,two
和three
来构建check
?我可以使用Validated
以及cats
的所有其他内容。
答案 0 :(得分:2)
注意:以下解决方案几乎不使用cats
。某些cats
专家可能会缩短它。
我认为用num
定义为你的目标是不可能实现的,因为它失去了状态:它会丢失在成功的情况下丢弃了多少项目。如果我们还需要返回列表的其余部分,使用(Option[String], List[Int])
作为返回类型似乎更容易:
def num(n: Int): List[Int] => (Option[String], List[Int]) = _ match {
case x :: xs => if (x == n) (None, (xs dropWhile (_ == n))) else (Some(s"expected $n"), xs)
case empty: List[Int] => (Some(s"expected $n"), empty)
}
现在,您可以创建构成检查的内容,例如:
def composedCheck(list: List[Int], checks: List[(List[Int]) => (Option[String], List[Int])]): Either[List[String], List[Int]] = {
val allChecksRes = checks.foldLeft((List.empty[String], list))((acc, check) => {
val checkRes = check(acc._2)
// shorter syntax but slower
//val errors = checkRes._1.toList ++ acc._1
// longer but without that much allocation
val errors = if (checkRes._1.isDefined) checkRes._1.get :: acc._1 else acc._1
(errors, checkRes._2)
})
if (allChecksRes._1.isEmpty) list.asRight else allChecksRes._1.reverse.asLeft
}
我认为返回Either[List[String], List[Int]]
是最自然的事情,让你以任何你喜欢的方式进一步处理错误,但也保留数据(原始列表),万一一切都很好。
最后,您可以将check
创建为
val one = num(1)
val two = num(2)
val three = num(3)
val check: List[Int] => Either[String, List[Int]] = l => composedCheck(l, List(one, two, three)).left.map(errors => errors.mkString(", "))
所有代码为一体:
import cats.implicits._
object CatsChecks extends App {
def num(n: Int): List[Int] => (Option[String], List[Int]) = _ match {
case x :: xs => if (x == n) (None, (xs dropWhile (_ == n))) else (Some(s"expected $n"), xs)
case empty: List[Int] => (Some(s"expected $n"), empty)
}
def composedCheck(list: List[Int], checks: List[(List[Int]) => (Option[String], List[Int])]): Either[List[String], List[Int]] = {
val allChecksRes = checks.foldLeft((List.empty[String], list))((acc, check) => {
val checkRes = check(acc._2)
// shorter syntax but slower
//val errors = checkRes._1.toList ++ acc._1
// longer but without that much allocation
val errors = if (checkRes._1.isDefined) checkRes._1.get :: acc._1 else acc._1
(errors, checkRes._2)
})
if (allChecksRes._1.isEmpty) list.asRight else allChecksRes._1.reverse.asLeft
}
val one = num(1)
val two = num(2)
val three = num(3)
val check: List[Int] => Either[String, List[Int]] = l => composedCheck(l, List(one, two, three)).left.map(errors => errors.mkString(", "))
println(check(Nil)) // error : "expected 1", "expected 2", "expected 3"
println(check(List(1, 1, 3))) // error : "expected 2"
println(check(List(1, 1, 4))) // errors: "expected 2", "expected 3"
println(check(List(3, 4, 5))) // errors: "expected 1", "expected 2"
println(check(List(0, 0, 0))) // errors: "expected 1", "expected 2", "expected 3"
println(check(List(1, 1, 2, 3, 3, 4)))
}
答案 1 :(得分:1)
听起来你想要的是Apply[Either[String, Unit], ?].map3(one, two, three) {
case (res1, res2, res3) => ...
}
,它为你提供了一系列arity函数,允许你链接仿函数。
kind-projector
我使用<?php foreach ($category_features as $category_feature):?>
<?php if( $category_feature['form_type'] == 'text' ){?>
<div class="six wide field">
<label><?php echo $category_feature['feauture'];?></label>
<input type="text" name="name-<?php echo $category_feature['id']?>">
</div>
<?php } ?>
<?php if( $category_feature['form_type'] == 'select' ){?>
<div class="six wide field">
<label><?php echo $category_feature['feauture'];?></label>
<select name="name-<?php echo $category_feature['id']?>">
<option value="">Choose</option>
<?php foreach ($feauture_values as $feauture_value):?>
<!-- how can i get the features' values -->
<?php endforeach;?>
</select>
</div>
<?php } ?>
<?php endforeach;?>
插件无需手动计算out类型,但除非你想实际使用可组合的东西派生出最终的仿函数,否则你想要应用&#34;中介&#34;步骤应用风格,这应该是方式。