如何有条件地应用一系列操作?

时间:2013-05-23 04:29:59

标签: scala

我有这样的逻辑:数据文件需要经历一系列操作,但是否需要操作是由一些布尔值控制的。基本上,数据数组经过过程循环并产生一些结果。

我想知道是否有一种优雅的方式对此进行编码,希望不使用var和许多if / else语句?

def op1(data): Seq[int]
def op2(data): Seq[int]
def op3(data): Seq[int]

def process(data: Seq[int], b1: boolean, b2: boolean, b3: boolean) = {

  //NOT working code, some thing does the following:  
  var temp: Seq[int] = data
  if (b1) 
    temp = op1(temp)
  if(b2)
    temp = op2(temp)
  if(b3)
    temp = op3(temp)

  temp

}

4 个答案:

答案 0 :(得分:1)

在这种情况下,我实际上更喜欢你的解决方案,但这是一个更实用的选择。我用字符串替换了Seq,因为它们更容易用于示例,但这无关紧要。

object SequenceOfOperations {
    def op1(data: String) = data + "A"            //> op1: (data: String)java.lang.String
    def op2(data: String) = data + "B"            //> op2: (data: String)java.lang.String
    def op3(data: String) = data + "C"            //> op3: (data: String)java.lang.String

    def process(data: String, b1: Boolean, b2: Boolean, b3: Boolean) = {
        val ops =Seq((b1 , op1(_)),(b2 , op2(_)),(b3 , op3(_)))

        val filteredOps = ops.filter( _._1).map (_._2)

        filteredOps.foldLeft(data)((l : String,o : String => String) => o(l))
    }                                             //> process: (data: String, b1: Boolean, b2: Boolean, b3: Boolean)String


    process("Z", true, false, true)               //> res0: String = ZAC
    process("Y", true, true, false)               //> res1: String = YAB
    process("X", false, false, true)              //> res2: String = XC
}

答案 1 :(得分:0)

这看起来像一个典型的折叠,您可以将其包装在以下函数中:

def maybeDo[A](bs: List[Boolean], fs: List[A => A], x: A) =
  (bs zip fs).foldLeft(x) {
    case (x, (b, f)) => if (b) f(x) else x
  }

使用它,例如如下

> val bs = List(true, false, true)
> val fs: List[Int => Int] = List(_+1, _*3, _+2)
> maybeDo(bs, fs, 10)
res0: Int = 13

在您的示例中,它将类似于

val temp = maybeFold(List(b1, b2, b3), List(op1 _, op2 _, op3 _), data)

答案 2 :(得分:0)

有很多方法可以实现这一目标。

一种可能的方式是......

 def op1(data: Seq[Int]) = data.map(_ + 2) //your actual operation here..
 def op2(data: Seq[Int]) = data.map(_ - 2) //..returning Seq[Int]
 def op3(data: Seq[Int]) = data.map(_ * 2) //

 implicit class SeqOps(val data: Seq[Int]) extends AnyVal {
   def op(op: Seq[Int] => Seq[Int], b: Boolean) = if (b) op(data) else data
 }

然后def process可写成..

def process(data: Seq[Int], b1: Boolean, b2: Boolean, b3: Boolean) = {
     data op (op1, b1) op (op2, b2) op (op3, b3)
}

答案 3 :(得分:0)

假设您希望流程签名不会改变......

// continuing from the ops psuedocode

def process(data: Seq[Int], b1:Boolean, b2:Boolean, b3:Boolean): Seq[Int] = {
  @tailrec
  def loop (acc: Seq[Int], ops: List[Seq[Int] => Seq[Int]]): Seq[Int] = {
    ops match {
      case List () => acc
      case head :: tail => loop (head(acc), tail)
    }
  }
  loop (data, List(( b1,op1 _), (b2,op2 _),(b3,op3 _)).filter{ _._1 }.map{ _._2 })
}

解决方案是将op与正确的布尔配对,在List中,过滤掉具有不真实布尔值的对,将过滤后的对映射到操作列表(基本上删除每个幸存对的布尔部分),然后累积您的输入数据递归转换。

以下是一些简单运算符的结果:     import scala.annotations.tailrec

def op1(s: Seq[Int]) = s map {_ + 1}
def op2(s: Seq[Int]) = s map {_ * 2}
def op3(s: Seq[Int]) = s map {_ - 5}

def process(data: Seq[Int], b1:Boolean, b2:Boolean, b3:Boolean): Seq[Int] = {
  @tailrec
  def loop (acc: Seq[Int], ops: List[Seq[Int] => Seq[Int]]): Seq[Int] = {
    ops match {
      case List () => acc
      case head :: tail => loop (head(acc), tail)
    }
  }
  loop (data, List(( b1,op1 _), (b2,op2 _),(b3,op3 _)).filter{ _._1 }.map{ _._2 })
}

process(Seq(1,2,3), true, false, true) // Seq[Int] = List(-3, -2, -1)
process(Seq(1,2,3), true, false, false) // Seq[Int] = List(2, 3, 4)
process(Seq(1,2,3), false, true, false) // Seq[Int] = List(2, 4, 6)