Как я могу сделать "if ..else" внутри для понимания?

StackOverflow https://stackoverflow.com/questions/4192227

  •  10-10-2019
  •  | 
  •  

Вопрос

Я задаю очень простой вопрос, который недавно сбил меня с толку.Я хочу написать Scala Для выражения, чтобы сделать что-то вроде следующего:

for (i <- expr1) {
  if (i.method) {
    for (j <- i) {
      if (j.method) {
        doSomething()
      } else {
        doSomethingElseA()
      }
    }
  } else {
    doSomethingElseB()
  }
}

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

for {i <- expr1
  if(i.method) // where can I write the else logic ?
  j <- i 
  if (j.method)
} doSomething()

Как я могу переписать код в стиле Scala?

Это было полезно?

Решение

Первый код, который вы написали, абсолютно корректен, так что переписывать его нет необходимости.В другом месте вы сказали, что хотели бы знать, как это сделать в стиле Scala.На самом деле не существует "стиля Scala", но я возьму на вооружение более функциональный стиль и придерживаюсь его.

for (i <- expr1) {
  if (i.method) {
    for (j <- i) {
      if (j.method) {
        doSomething()
      } else {
        doSomethingElseA()
      }
    }
  } else {
    doSomethingElseB()
  }
}

Первая проблема заключается в том, что это не возвращает никакого значения.Все, что он вызывает, - это побочные эффекты, которых также следует избегать.Итак, первое изменение было бы примерно таким:

val result = for (i <- expr1) yield {
  if (i.method) {
    for (j <- i) yield {
      if (j.method) {
        returnSomething()
        // etc

Так вот, есть большая разница между

for (i <- expr1; j <- i) yield ...

и

for (i <- expr1) yield for (j <- i) yield ...

Они возвращают разные вещи, и бывают моменты, когда вы хотите более поздние, а не прежние.Хотя я предполагаю, что вы хотите первое.Теперь, прежде чем мы продолжим, давайте исправим код.Это некрасиво, трудно для понимания и неинформативно.Давайте реорганизуем его с помощью методов извлечения.

def resultOrB(j) = if (j.method) returnSomething else returnSomethingElseB
def nonCResults(i) = for (j <- i) yield resultOrB(j)
def resultOrC(i) = if (i.method) nonCResults(i) else returnSomethingC
val result = for (i <- expr1) yield resultOrC(i)

Это уже намного чище, но возвращается не совсем то, что мы ожидаем.Давайте посмотрим на разницу:

trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"

def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else Unrecognized
val result = for (i <- expr1) yield classifyElements(i)

Тип result есть Array[AnyRef], в то время как использование нескольких генераторов дало бы Array[Element].Самая простая часть исправления заключается в следующем:

val result = for {
  i <- expr1
  element <- classifyElements(i)
} yield element

Но это само по себе не сработает, потому что сам classifyElements возвращает AnyRef, и мы хотим, чтобы он вернул коллекцию.Сейчас, validElements верните коллекцию, так что это не проблема.Нам нужно только исправить else часть.С тех пор как validElements возвращает IndexedSeq, давайте вернемся к этому на else тоже часть.Конечным результатом является:

trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"

def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else IndexedSeq(Unrecognized)
val result = for {
  i <- expr1
  element <- classifyElements(i)
} yield element

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

О Доходности

Я думаю, важно отметить одну вещь о представленной проблеме.Давайте упростим это:

for (i <- expr1) {
  for (j <- i) {
    doSomething
  }
}

Теперь это реализовано с помощью foreach (см . здесь, или другие подобные вопросы и ответ на них).Это означает, что приведенный выше код делает точно то же самое, что и этот код:

for {
  i <- expr1
  j <- i
} doSomething

В точности то же самое.Это совсем не так, когда кто-то использует yield.Следующие выражения не дают такого же результата:

for (i <- expr1) yield for (j <- i) yield j

for (i <- expr1; j <- i) yield j

Первый фрагмент будет реализован с помощью двух map вызывает, в то время как второй фрагмент будет использовать один flatMap и один map.

Таким образом, это происходит только в контексте yield что вообще имеет какой-то смысл беспокоиться о вложенности for циклы или использование нескольких генераторов.И, на самом деле, генераторы обозначает тот факт , что что - то находится в сгенерированный, что верно только для истинных представлений (тех yieldчто-то пишущий).

Другие советы

Часть

for (j <- i) {
   if (j.method) {
     doSomething(j)
   } else {
     doSomethingElse(j)
   }
 }

может быть переписан как

for(j <- i; e = Either.cond(j.method, j, j)) {
  e.fold(doSomething _, doSomethingElse _)  
}  

(Конечно, вы можете использовать урожайность вместо этого, если вы делаете, методы что -то возвращают)

Здесь это не так ужасно полезно, но если у вас есть более глубокая вложенная структура, она может ...

import scalaz._; import Scalaz._

val lhs = (_ : List[X]) collect { case j if j.methodJ => doSomething(j) } 
val rhs = (_ : List[X]) map doSomethingElse
lhs <-: (expr1 partition methodI) :-> rhs

Вы не можете. Для (expr; if) конструкция просто фильтровать элемент, который должен обрабатывать в цикле.

Если заказ не важен для вызовов Dosomething () и doSomethingElse (), вы можете изменить такой код.

val (tmp, no1) = expr1.partition(_.method)
val (yes, no2) = tmp.partition(_.method)

yes.foreach(doSomething())
no1.foreach(doSomethingElse())
no2.foreach(doSomethingElse())

Чтобы ответить на ваш первоначальный вопрос, я думаю, что для понимания может быть довольно хорошим для конкретных вариантов использования, и ваш пример не подходит.

Условия, указанные в Scala для операции, для фильтрации элементов из генераторов. Элементы, не удовлетворяющие условиям, отбрасываются и не представлены в блоке доходности / кода.

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

Также имейте в виду, что для работы для работы относительно дорого (в настоящее время), поэтому, возможно, более простой итеративный подход может быть более подходящим, возможно, что -то вроде:

expr1 foreach {i =>
  if (i.method) {
    i foreach {j =>
      if (j.method)
        doSomething()
      else
        doSomethingElseA()
    }
  }
  else
    doSomethingElseB()
}

Обновлять:

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

for (i <- expr1; j <- i) {
  if (i.method) {if (j.method) doSomething() else doSomethingElseA()} else doSomethingElseB()
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top