有条件地在Scala中应用函数 - 如何编写此函数?

时间:2015-05-22 20:21:05

标签: scala

我需要有条件地将函数f1应用于集合中的元素,具体取决于函数f2的结果,该函数将每个元素作为参数并返回一个布尔值。如果f2(e)为真,则将应用f1(e),否则“e”将“按原样”返回。 我的目的是编写一个能够处理任何类型集合的通用函数。

c: C[E] // My collection 
f1 = ( E => E )  // transformation function
f2 = ( E => Boolean )  // conditional function

我无法找到解决方案。这是我的想法,但我担心我在高水域

/* Notice this code doesn't compile ~ partially pseudo-code */
conditionallyApply[E,C[_](c: C[E], f2: E => Boolean, f1: E => E): C[E] = {
  @scala.annotation.tailrec
  def loop(a: C[E], c: C[E]): C[E] = {
    c match {
      case Nil => a // Here head / tail just express the idea, but I  want to use a generic collection
      case head :: tail => go(a ++ (if f2(head) f1(head) else head ), tail)
    }
  }
  loop(??, c)  // how to get an empty collection of the same type as the one from the input?
}

你们中有人可以启发我吗?

4 个答案:

答案 0 :(得分:3)

这看起来像map的简单Functor。使用scalaz

def condMap[F[_],A](fa: F[A])(f: A => A, p: A => Boolean)(implicit F:Functor[F]) =
  F.map(fa)(x => if (p(x)) f(x) else x)

答案 1 :(得分:2)

不知道为什么你需要scalaz才能行走。

// example collection and functions
val xs = 1 :: 2 :: 3 :: 4 :: Nil
def f1(v: Int) = v + 1
def f2(v: Int) = v % 2 == 0                     

// just conditionally transform inside a map
val transformed = xs.map(x => if (f2(x)) f1(x) else x)  

答案 2 :(得分:0)

不使用scalaz,您可以使用CanBuildFrom模式。这正是标准集合库中使用的内容。当然,在您的具体情况下,这可能是过度设计的,因为对map的简单调用就足够了。

import scala.collection.generic._

def cmap[A, C[A] <: Traversable[A]](col: C[A])(f: A ⇒ A, p: A ⇒ Boolean)(implicit bf: CanBuildFrom[C[A], A, C[A]]): C[A] = {
  val b = bf(col)
  b.sizeHint(col)
  for (x <- col) if(p(x)) b += f(x) else b += x
  b.result
}

现在用法:

scala> def f(i: Int) = 0
f: (i: Int)Int

scala> def p(i: Int) = i % 2 == 0
p: (i: Int)Boolean

scala> cmap(Seq(1, 2, 3, 4))(f, p)
res0: Seq[Int] = List(1, 0, 3, 0)

scala> cmap(List(1, 2, 3, 4))(f, p)
res1: List[Int] = List(1, 0, 3, 0)

scala> cmap(Set(1, 2, 3, 4))(f, p)
res2: scala.collection.immutable.Set[Int] = Set(1, 0, 3)

观察返回类型如何始终与提供的类型相同。

该函数可以很好地封装在一个隐式类中,使用&#34; pimp my library&#34;图案。

答案 3 :(得分:0)

对于这样的事情,您可以使用隐式类。出于这个原因添加它们是为了增强您无法更改的库。

它会像这样工作:


object ImplicitStuff {
  implicit class SeqEnhancer[A](s:Seq[A])  {
    def transformIf(  cond : A => Boolean)( f : A => A ):Seq[A] =
      s.map{ x => if(cond(x)) f(x)  else x }
  }

  def main(a:Array[String]) = {
    val s = Seq(1,2,3,4,5,6,7)

    println(s.transformIf(_ % 2 ==0){ _ * 2})
    // result is (1, 4, 3, 8, 5, 12, 7)
  }
}

基本上,如果您调用的对象中不存在某个方法(在这种情况下为Seq),它将检查是否存在实现该方法的隐式类,但是看起来内置方法。