2014-07-24 26 views
2

假设有一个List[A]和两个谓词p1: A => Booleanp2: A => Boolean
我需要在列表中找到两个要素:a1满足p1及第一要素a2满足p2的第一个元素(在我的情况a1 != a2如何在Scala中查找满足两个谓词的两个元素?

很显然,我可以运行find两次,但我想这样做的一通。你如何在Scala中通过一次?

+4

我敢打赌,导致代码将比两遍更模糊。只是说。 –

+0

我想尾部递归函数会非常清晰。 – Michael

+1

答案是折叠。答案总是一堆:)我会在会议结束后添加一个答案,但是'myList.foldLeft((None,None)){case((a1,a2),e)=>(if(!a1。 (e)其他a1,if(!a2.isDefined && p2(e))Some(e)else a2)}'应该近距离 –

回答

1

因此,这里有一个尝试。这是相当直接推广其采取谓词的列表(并返回找到的元素列表)

def find2[A](xs: List[A], p1: A => Boolean, p2: A => Boolean): (Option[A], Option[A]) = { 
    def find2helper(xs: List[A], p1: A => Boolean, p2: A => Boolean, soFar: (Option[A], Option[A])): (Option[A], Option[A]) = { 
    if (xs == Nil) soFar 
    else { 
     val a1 = if (soFar._1.isDefined) soFar._1 else if (p1(xs.head)) Some(xs.head) else None 
     val a2 = if (soFar._2.isDefined) soFar._2 else if (p2(xs.head)) Some(xs.head) else None 
     if (a1.isDefined && a2.isDefined) (a1, a2) else find2helper(xs.tail, p1, p2, (a1, a2)) 
    } 
    } 
    find2helper(xs, p1, p2, (None, None)) 

} //> find2: [A](xs: List[A], p1: A => Boolean, p2: A => Boolean)(Option[A], Option[A]) 

    val foo = List(1, 2, 3, 4, 5) //> foo : List[Int] = List(1, 2, 3, 4, 5) 

    find2[Int](foo, { x: Int => x > 2 }, { x: Int => x % 2 == 0 }) 
    //> res0: (Option[Int], Option[Int]) = (Some(3),Some(2)) 
    find2[Int](foo, { x: Int => x > 2 }, { x: Int => x % 7 == 0 }) 
    //> res1: (Option[Int], Option[Int]) = (Some(3),None) 
    find2[Int](foo, { x: Int => x > 7 }, { x: Int => x % 2 == 0 }) 
    //> res2: (Option[Int], Option[Int]) = (None,Some(2)) 
    find2[Int](foo, { x: Int => x > 7 }, { x: Int => x % 7 == 0 }) 
    //> res3: (Option[Int], Option[Int]) = (None,None) 

广义的版本(这实际上是略微清晰,我认为)

def findN[A](xs: List[A], ps: List[A => Boolean]): List[Option[A]] = { 
    def findNhelper(xs: List[A], ps: List[A => Boolean], soFar: List[Option[A]]): List[Option[A]] = { 
    if (xs == Nil) soFar 
    else { 
     val as = ps.zip(soFar).map { 
      case (p, e) => if (e.isDefined) e else if (p(xs.head)) Some(xs.head) else None 
     } 
     if (as.forall(_.isDefined)) as else findNhelper(xs.tail, ps, as) 
    } 
    } 
    findNhelper(xs, ps, List.fill(ps.length)(None)) 

} //> findN: [A](xs: List[A], ps: List[A => Boolean])List[Option[A]] 

val foo = List(1, 2, 3, 4, 5) //> foo : List[Int] = List(1, 2, 3, 4, 5) 

findN[Int](foo, List({ x: Int => x > 2 }, { x: Int => x % 2 == 0 })) 
//> res0: List[Option[Int]] = List(Some(3), Some(2)) 
findN[Int](foo, List({ x: Int => x > 2 }, { x: Int => x % 7 == 0 })) 
//> res1: List[Option[Int]] = List(Some(3), None) 
findN[Int](foo, List({ x: Int => x > 7 }, { x: Int => x % 2 == 0 })) 
//> res2: List[Option[Int]] = List(None, Some(2)) 
findN[Int](foo, List({ x: Int => x > 7 }, { x: Int => x % 7 == 0 })) 
//> res3: List[Option[Int]] = List(None, None) 
1
scala> val l = List(1,2,3) 
l: List[Int] = List(1, 2, 3) 

scala> val p1 = {x:Int => x % 2 == 0} 
p1: Int => Boolean = <function1> 

scala> val p2 = {x:Int => x % 3 == 0} 
p2: Int => Boolean = <function1> 

scala> val pp = {x:Int => p1(x) || p2(x) } 
pp: Int => Boolean = <function1> 

scala> l.find(pp) 
res2: Option[Int] = Some(2) 

scala> l.filter(pp) 
res3: List[Int] = List(2, 3) 
+0

谢谢。但是我不知道你的例子中的'filtered'列表(列表(2,3))中的哪个元素满足'p1',哪个满足'p2'。 – Michael

1

这是否适合您?

def predFilter[A](lst: List[A], p1: A => Boolean, p2: A => Boolean): List[A] = 
    lst.filter(x => p1(x) || p2(x)) // or p1(x) && p2(x) depending on your need 

这将返回一个匹配任一谓词的新列表。

val a = List(1,2,3,4,5) 
val b = predFilter[Int](a, _ % 2 == 0, _ % 3 == 0) // b is now List(2, 3, 4) 
+0

不应该是p1(x)|| p2(x)? – Ashalynd

+0

@Ashalynd依赖于使用,当我使用|| '_> 2'和'_ <5'匹配整个列表,因为除了5以外所有元素都小于5,并且5与“大于2”匹配 –

+0

OP正在讨论满足谓词。 – Ashalynd

相关问题