Scala单片眼镜中的过滤列表 [英] Filtering Lists in Scala's Monocle

查看:99
本文介绍了Scala单片眼镜中的过滤列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码:

case class Person(name :String)
case class Group(group :List[Person])

val personLens = GenLens[Person]
val groupLens = GenLens[Group]

我该如何从选择中过滤"某些人,而不是通过索引,而不是通过Person的特定属性,例如:

how can i "filter" out certain Persons from the selection, NOT by index but by a specific property of Person, like:

val trav :Traversal[Group, Person] = (groupLens(_.group) composeTraversal filterWith((x :Person) => /*expression of type Boolean here */))

我只找到了filterIndex函数,该函数仅包括基于索引的列表中的元素,但这不是我想要的.

I only found the filterIndex function, which does only include elements from the list based on index, but this is not what i want.

filterIndex具有类型为(Int => Boolean)

我想要:

filterWith(组成名称),它取一个(x => Boolean),其中x为列表元素的类型,在此简短示例中为Person.

filterWith (made up name), that takes a (x => Boolean), where x has the type of the lists element, namely Person in this short example.

这似乎非常实用和普遍,以至于我假设有人对此进行了思考,而我(我必须承认我对此事的理解有限)不明白为什么无法做到这一点.

This seems so practical and common that i assume somebody has thought about that and i (with my, i must admit limited understanding of the matter) don't see why it can't be done.

我是否缺少此功能,是尚未实现,还是出于某种原因而根本无法实现(请在时间允许的情况下进行解释).

Am i missing this functionality, is it not implemented yet or just plainly impossible for whatever reason (please do explain if you have the time).

谢谢.

推荐答案

错误的版本

我将从天真的尝试开始,像这样写.我在这里使用一个简单的列表版本,但是如果您愿意的话,您可能会更喜欢(使用Traverse或其他工具).

import monocle.Traversal
import scalaz.Applicative, scalaz.std.list._, scalaz.syntax.traverse._

def filterWith[A](p: A => Boolean): Traversal[List[A], A] =
  new Traversal[List[A], A] {
    def modifyF[F[_]: Applicative](f: A => F[A])(s: List[A]): F[List[A]] =
      s.filter(p).traverse(f)
  }

然后:

import monocle.macros.GenLens

case class Person(name: String)
case class Group(group: List[Person])

val personLens = GenLens[Person]
val groupLens = GenLens[Group]

val aNames = groupLens(_.group).composeTraversal(filterWith(_.name.startsWith("A")))

val group = Group(List(Person("Al"), Person("Alice"), Person("Bob")))

最后:

scala> aNames.getAll(group)
res0: List[Person] = List(Person(Al), Person(Alice))

有效!

它有效,除了……

scala> import monocle.law.discipline.TraversalTests
import monocle.law.discipline.TraversalTests

scala> TraversalTests(filterWith[String](_.startsWith("A"))).all.check
+ Traversal.get what you set: OK, passed 100 tests.
+ Traversal.headOption: OK, passed 100 tests.
! Traversal.modify id = id: Falsified after 2 passed tests.
> Labels of failing property: 
Expected List(崡) but got List()
> ARG_0: List(崡)
! Traversal.modifyF Id = Id: Falsified after 2 passed tests.
> Labels of failing property: 
Expected List(ᜱ) but got List()
> ARG_0: List(ᜱ)
+ Traversal.set idempotent: OK, passed 100 tests.

五分之三不是很好.

让我们重新开始:

def filterWith2[A](p: A => Boolean): Traversal[List[A], A] =
  new Traversal[List[A], A] {
    def modifyF[F[_]: Applicative](f: A => F[A])(s: List[A]): F[List[A]] =
      s.traverse {
        case a if p(a) => f(a)
        case a => Applicative[F].point(a)
      }
  }

val aNames2 = groupLens(_.group).composeTraversal(filterWith2(_.name.startsWith("A")))

然后:

scala> aNames2.getAll(group)
res1: List[Person] = List(Person(Al), Person(Alice))

scala> TraversalTests(filterWith2[String](_.startsWith("A"))).all.check
+ Traversal.get what you set: OK, passed 100 tests.
+ Traversal.headOption: OK, passed 100 tests.
+ Traversal.modify id = id: OK, passed 100 tests.
+ Traversal.modifyF Id = Id: OK, passed 100 tests.
+ Traversal.set idempotent: OK, passed 100 tests.

好的,更好!

Traversal真实"法律不是'在Monocle的TraversalLaws中编码(目前至少不是 ),并且我们还想要类似的东西要保持的状态:

The "real" laws for Traversal aren't encoded in Monocle's TraversalLaws (at least not at the moment), and we additionally want something like this to hold:

对于任何f: A => Ag: A => At.modify(f.compose(g))应等于t.modify(f).compose(t.modify(g)).

让我们尝试一下:

scala> val graduate: Person => Person = p => Person("Dr. " + p.name)
graduate: Person => Person = <function1>

scala> val kill: Person => Person = p => Person(p.name + ", deceased")
kill: Person => Person = <function1>

scala> aNames2.modify(kill.compose(graduate))(group)
res2: Group = Group(List(Person(Dr. Al, deceased), Person(Dr. Alice, deceased), Person(Bob)))

scala> aNames2.modify(kill).compose(aNames2.modify(graduate))(group)
res3: Group = Group(List(Person(Dr. Al), Person(Dr. Alice), Person(Bob)))

所以我们再没有运气了. filterWith实际上合法的唯一方法是,如果我们保证绝不将其与modify的参数一起使用,这可能会改变谓词的结果.

So we're out of luck again. The only way our filterWith could actually be lawful is if we promise never to use it with an argument to modify that might change the result of the predicate.

这就是为什么filterIndex合法的原因-它的谓词将modify不能触及的东西作为参数,因此您不能违反t.modify(f.compose(g)) === t.modify(f).compose(t.modify(g))律.

This is why filterIndex is legit—its predicate takes as an argument something that modify can't touch, so you can't break the t.modify(f.compose(g)) === t.modify(f).compose(t.modify(g)) law.

您可以编写一个非法的Traversal来进行非法的过滤工作并一直使用,很可能它永远不会伤害您,而且没人会认为您是一个可怕的人.因此,如果需要的话,就去吧.不过,您可能永远不会在像样的镜头中看到filterWith.

You could write an unlawful Traversal that does unlawful filtering stuff and use it all the time and it's pretty likely that it will never hurt you and that nobody will ever think you are a horrible person. So go for it, if you want. You'll probably never see a filterWith in a decent lens library, though.

这篇关于Scala单片眼镜中的过滤列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆