Scala如何在某些覆盖条件下避免var

时间:2018-10-23 08:06:19

标签: scala functional-programming

功能性编程风格的编码指南指出,为了更好的功能性编程代码,我们不应在Scala中使用nullvar

我想执行以下操作

    var a = 2
    if(condition==true){
     a = a * 3 /*someOperation*/
    }

   if(condition2==true){
     a = a * 6 /*someOperation*/
    }

   if(condition3==true){
     a = a * 8 /*someOperation*/
    }
    val b = a * 2/*someOperation*/

那么现在如何避免在这种情况下使用var并将其替换为val?

7 个答案:

答案 0 :(得分:2)

避免带有多个条件的var的最简单方法是使用临时值

val a1 = 2
val a2 = if (condition)  a1*3 else a1
val a3 = if (condition2) a2*6 else a2
val a  = if (condition3) a3*8 else a3

val b = a * 2/*someOperation*/

在真实代码中,您将使用a1a2a3有意义的名称来描述每个处理阶段的结果。

如果您不希望在范围内包含这些额外的值,请将其放在一个块中:

val a = {
  val a1 = 2
  val a2 = if (condition)  a1*3 else a1
  val a3 = if (condition2) a2*6 else a2
  if (condition3) a3*8 else a3
}

更新

如果您想要一种更实用的方法,请收集条件和修改内容,然后依次应用它们,如下所示:

val mods: List[(Boolean, Int=>Int)] = List(
  (condition,  _*3),
  (condition2, _*6),
  (condition3, _*8)
)

val a = mods.foldLeft(2){ case (a,(cond, mod)) => if (cond) mod(a) else a }

这仅在条件或修改更为复杂时才适用,并且将它们保持在一起可使代码更清晰。

答案 1 :(得分:1)

{"error":
    {
    "errors":[
      {
        "domain":"global",
        "reason":"required",
        "message":"Login Required",
        "locationType":"header",
        "location":"Authorization"
      }
    ],
    "code":401,
    "message":"Login Required"
    }
}

或者,也许...

val a = 2 * (if (condition) 3 else 1)
val b = 2 * a

这取决于在执行这些操作后是否/如何使用val a = 2 val b = 2 * (if (condition) a*3 else a)

答案 2 :(得分:1)

我想您可能已经简化了您的示例,因为我们在编写代码时知道a的值,因此您可以这样写出来:

val a = if (condition) 2 else 6
val b = a * 2

假设您的实际操作更加复杂并且无法进行这样的预先计算,那么您可能会发现这样的模式匹配是一种更好的方法:

val a = (condition, 2) match {
  case (true, z) => 
    z * 3
  case (false, z) => 
    z
}
val b = a * 2

答案 3 :(得分:1)

您可以尝试以下方法:

type Modification = Int => Int
type ModificationNo = Int
type Conditions = Map[ModificationNo, Boolean]

val modifications: List[(Modification, ModificationNo)] = 
    List[Modification](
        a => a * 3, 
        a => a * 6, 
        a => a * 8
    ).zipWithIndex

def applyModifications(initial: Int, conditions: Conditions): Int =
  modifications.foldLeft[Int](initial) {
    case (currentA, (modificationFunc, modificationNo)) =>
      if (conditions(modificationNo)) modificationFunc(currentA)
      else currentA
  }

val a: Int = applyModifications(initial = 2, 
  conditions = Map(0 -> true, 1 -> false, 2 -> true))

看似复杂,但是如果条件数量足够大,此方法可以提供更大的灵活性。 现在,当您必须添加更多条件时,您无需编写新的if语句和对var的进一步重新分配。只需在现有

列表中添加修改功能

答案 4 :(得分:0)

重要的一点是,FP是一种全新的编程范例。它的根本差异如此之大,以至于有时您无法摘录imperative代码并将其转换为functional代码。

差异不仅适用于代码,而且适用于解决问题的思维方式。函数式编程要求您考虑链式数学计算,它们彼此或多或少相互独立(这意味着这些数学计算中的每一个都不应在其自身环境之外进行任何更改)。

功能编程完全避免了状态突变。因此,如果您的解决方案要求变量x在某个点具有值10,而在另一点具有其他值100,那么您的解决方案就不是{ {1}}。而且您无法为functional的解决方案编写function代码。

现在,如果您查看案件(假设您实际上并不需要not functionala,然后在一段时间后更改为2),然后尝试将其转换为独立的数学计算,然后是最简单的数学计算,

6

答案 5 :(得分:0)

没有完美的解决方案。
有时如果简化代码并限制单个函数的范围,可以使用var

话虽如此,这就是我将以功能性方式进行的方式:

val op1: Int => Int =
if (condition1) x => x * 3
else identity

val op2: Int => Int =
  if (condition2) x => x * 6
  else identity

val op3: Int => Int =
  if (condition3) x => x * 8
  else identity


val op = op1 andThen op2 andThen op3
// can also be written as 
// val op = Seq(op1, op2, op3).reduceLeft(_ andThen _)

val a = 2
val b = op(a) * 2

答案 6 :(得分:0)

将变量包装为monad的最简单方法,以便您.map在其上。最简单的monad是Option,因此您可以编写:

val result = Option(a).map { 
  case a if condition => a*2
  case a => a
}.map { 
  case a if condition2 => a*6
  case a => a
}.fold(a) { 
  case a if condition3 => a*8
  case a => a
}

(最后一个操作是fold而不是map,因此最终得到的是结果的“原始”值,而不是选项。等效地,您可以将其写为{{ 1}},然后在末尾添加.map

当您有很多这样的条件操作,或者必须重复模式的许多用例时,将它们放入列表,然后遍历该列表可能会有所帮助:

.getOrElse(a)