Функциональный способ обновления объекта на основе флагов - PullRequest
1 голос
/ 23 февраля 2012

предположим, что вы пишете класс, который нормализует строки. Этот класс имеет несколько флагов конфигурации. Например:

   val makeLowerCase: Boolean = true
   val removeVowels: Boolean = false
   val dropFirstCharacter: Boolean = true

Если бы я писал изменчивый код, я бы написал следующее для метода normalize.

def normalize(string: String) = {
  var s = string

  if (makeLowerCase) {
    s = s.toLowerCase
  }

  if (removeVowels) {
    s = s.replaceAll("[aeiou]", "")
  }

  if (dropFirstCharacter) {
    s = s.drop(1)
  }

  s
}

Есть ли чистый и простой способ написать это без мутации? Вложенные условные выражения быстро становятся неприятными. Я мог бы создать список String=>String лямбд, отфильтровать его по конфигурации и затем свернуть строку через него, но я надеюсь, что есть что-то более простое.

Ответы [ 2 ]

6 голосов
/ 23 февраля 2012

Лучше всего определить свой собственный метод:

class ConditionalMapper[A](a: A) {
  def changeCheck(p: A => Boolean)(f: A => A) = if (p(a)) f(a) else a
  def changeIf(b: Boolean)(f: A => A) = if (b) f(a) else a
}
implicit def conditionally_change_anything[A](a: A) = new ConditionalMapper(a)

Теперь вы объедините эти вещи вместе и напишите:

class Normer(makeLC: Boolean, remVowel: Boolean, dropFirst: Boolean) {
  def normalize(s: String) = {
    s.changeIf(makeLC)   { _.toLowerCase }
     .changeIf(remVowel) { _.replaceAll("[aeiou]","") }
     .changeIf(dropFirst){ _.substring(1) }
  }
}

Что даст вам:

scala> val norm = new Normer(true,false,true)
norm: Normer = Normer@2098746b

scala> norm.normalize("The Quick Brown Fox Jumps Over The Lazy Dog")
res1: String = he quick brown fox jumps over the lazy dog

Тем не менее, изменяемое решение тоже неплохое - просто держите его в маленьком блоке, и все будет в порядке.Это в основном проблема, когда вы позволяете изменчивости уйти в дикую природу.(Где «дикий» означает «вне вашего метода или внутри любого метода, длина которого превышает несколько строк».)

4 голосов
/ 23 февраля 2012

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

case class N(
  makeLowerCase: Boolean = true,
  removeVowels: Boolean = false,
  dropFirstCharacter: Boolean = true) {

  def normalize(string: String) = (
    string 
      |> (s => if (makeLowerCase) s.toLowerCase else s) 
      |> (s => if (removeVowels) s.replaceAll("[aeiou]", "") else s) 
      |> (s => if (dropFirstCharacter) s.drop(1) else s) 
  )

}

N(removeVowels=true).normalize("DDABCUI")
// res1: String = dbc
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...