Scala, как избежать var при некоторых условиях переопределения - PullRequest
0 голосов
/ 23 октября 2018

Стиль функционального программирования в руководстве по кодированию гласит, что мы не должны использовать null или var в Scala для улучшения функционального кода программирования.

Я хочу выполнить некоторую операцию, например ниже

    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 голосов
/ 23 октября 2018

1 идеального решения не существует.
иногда можно использовать 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
0 голосов
/ 23 октября 2018

Самый простой способ это обернуть вашу переменную в монаду, чтобы вы .map над ней.Самая простая монада - это 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, так что вы получите «необработанное» значение для результата, а не вариант. Эквивалентно, вы могли бы написать его как .map, а затем добавить .getOrElse(a) в конце).

Когда у вас есть много таких условных операций или во многих случаях использования, когда вынеобходимо повторить шаблон, это может помочь поместить их в список, а затем просмотреть этот список:

def applyConditionals[T](toApply: (() => Boolean, T => T)*) = toApply
  .foldLeft(a) { 
     case (a, (cond, oper)) if cond() => oper(a)
     case (a, _) => a
  }

val result = applyConditionals[Int] (
   (() => condition,  _ * 2),
   (() => condition2, _ * 6),
   (() => condition3, _ * 8)
)
0 голосов
/ 23 октября 2018

Самый простой способ избежать 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*/

В реальном коде вы бы дали a1, a2 и a3 значимые имена дляопишите результат каждого этапа обработки.

Если вас беспокоит наличие этих дополнительных значений в области видимости, поместите его в блок:

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 }

Это действительно уместно только тогда, когда условия или модификации являются более сложными, и сохраняя ихвместе делает код понятнее.

0 голосов
/ 23 октября 2018

Вы можете попробовать следующий подход:

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.Просто добавьте функцию модификации в существующий список

0 голосов
/ 23 октября 2018

Важным моментом является то, что FP - это совершенно новая парадигма программирования.Он настолько принципиально отличается, что иногда вы не можете взять отрывок из imperative кода и попытаться преобразовать его в functional код.

Разница относится не только к коду, но и к мышлению, направленному на решениепроблема.Функциональное программирование требует, чтобы вы мыслили в терминах цепочечных математических вычислений, которые более или менее независимы друг от друга (что означает, что каждое из этих математических вычислений не должно ничего менять вне своей собственной среды).

Функциональное программирование полностью исключает изменение состояния.Итак, если ваше решение требует наличия переменной x, которая имеет значение 10 в одной точке и другое значение 100 в какой-то другой точке ... тогда ваше решение не является functional.И вы не можете написать function код для решения, которое является not functional.

Теперь, если вы посмотрите на ваш случай (предполагая, что вам на самом деле не нужно a, чтобы быть 2, а затем изменитена 6 через некоторое время) и попытаться преобразовать его в цепочку независимых математических вычислений, тогда самый простой следующий:

val a = if (condition) 2 else 6
val b = a * 2
0 голосов
/ 23 октября 2018

Я думаю, вы могли бы упростить свой пример, потому что мы знаем значение 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
0 голосов
/ 23 октября 2018
val a = 2 * (if (condition) 3 else 1)
val b = 2 * a

Или, возможно ...

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

Зависит от того, используется ли / как a после этих операций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...