加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

通过在Scala中组合Option [谓词]函数来构建最小谓词函数(可能使

发布时间:2020-12-16 09:57:29 所属栏目:安全 来源:网络整理
导读:我有一个列表,我希望过滤结果. 用户可以为行上的任何属性提供特定限制(例如,我只想查看x == 1的行).如果它们没有指定限制,那么当然不使用谓词.当然,最简单的形式是: list.filter(_.x == 1) 有许多可能的简单谓词,我正在构建一个新的谓词函数,其中包含将用户
我有一个列表,我希望过滤结果.

用户可以为行上的任何属性提供特定限制(例如,我只想查看x == 1的行).如果它们没有指定限制,那么当然不使用谓词.当然,最简单的形式是:

list.filter(_.x == 1)

有许多可能的简单谓词,我正在构建一个新的谓词函数,其中包含将用户搜索项(例如Option [Int])转换为谓词函数或Identity(返回true的函数)的代码.代码看起来像这样(缩短了,为了清楚起见添加了显式类型):

case class ResultRow(x: Int,y: Int)

object Main extends App {
  // Predicate functions for the specific attributes,along with debug output
  val xMatches = (r: ResultRow,i: Int) => { Console println "match x"; r.x == i }
  val yMatches = (r: ResultRow,i: Int) => { Console println "match y"; r.y == i }
  val Identity = (r : ResultRow) => { Console println "identity"; true }

  def makePredicate(a: Option[Int],b: Option[Int]) : ResultRow => Boolean = {
    // The Identity entry is just in case all the optional params are None 
    // (otherwise,flatten would cause reduce to puke)
    val expr = List(Some(Identity),a.map(i => xMatches(_: ResultRow,i)),b.map(i => yMatches(_: ResultRow,i))
                   ).flatten

    // Reduce the function list into a single function. 
    // Identity only ever appears on the left...
    expr.reduceLeft((a,b) => (a,b) match {
      case (Identity,f) => f
      case (f,f2) => (r: ResultRow) => f(r) && f2(r)
    })
  }

  val rows = List(ResultRow(1,2),ResultRow(3,100))

  Console println rows.filter(makePredicate(Some(1),None))
  Console println rows.filter(makePredicate(None,Some(100)))
  Console println rows.filter(makePredicate(Some(3),Some(100)))
}

这非常有效.运行时,它会正确过滤,调试输出证明调用最少数量的函数来适当地过滤列表:

match x
match x
List(ResultRow(1,2))
identity
identity
List(ResultRow(1,100))
match y
match y
List(ResultRow(3,100))
match x
match x
match y
List(ResultRow(3,100))

我真的很高兴这出来了.

但是,我不禁想到有一种更实用的方法(例如Monoids和Functors以及广义总和)……但我无法弄清楚如何使它工作.

我尝试了一个scalaz示例,表明我需要创建一个隐含的零和半群,但我无法得到零[ResultRow => Boolean]进行类型检查.

解决方法

您可以使用forall方法简化代码(无需转移到Scalaz):

def makePredicate(a: Option[Int],b: Option[Int]): ResultRow => Boolean = {
  val expr = List(
    a.map(i => xMatches(_: ResultRow,i))
  ).flatten

  (r: ResultRow) => expr.forall(_(r))
}

请注意,这也消除了在列表中包含Some(Identity)的需要.

如果你有很多行,我建议使用zip来匹配xMatches函数和用户输入,如下所示:

val expr = List(a,b) zip List(xMatches,yMatches) flatMap {
  case (maybePred,matcher) => maybePred.map(i => matcher(_: ResultRow,i))
}

它实际上不是更简洁或可读两行,而是四或五.

要回答关于Scalaz的问题,问题是布尔有两个可能的幺半群,而Scalaz不会为你选择一个 – 而是你必须用Haskell的newtype包装器来标记你的布尔值,以指示你想要哪个幺半群使用(在Scalaz 7-in 6中,方法略有不同).

一旦你指出了你想要布尔的monoid,Function1的monoid将会启动,而且没有什么可做的了 – 你不需要明确地定义一个Identity零.例如:

import scalaz._,Scalaz._

def makePredicate(a: Option[Int],b: Option[Int]): ResultRow => Boolean =
  List(a,b).zip(List(xMatches,yMatches)).flatMap {
    case (maybePred,matcher) =>
      maybePred.map(i => matcher(_: ResultRow,i).conjunction)
  }.suml

这里我们刚刚得到了ResultRow =>的总和. Boolean @@连接函数.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读