Стрелка антипаттерн является стандартом в Scala

Первый вопрос: является ли стрелка антипаттерном в Scala?

Я перехожу с Java на Scala около 3 месяцев. Я начинаю понимать, что шаблон «анти-стрелка» является чем-то вроде стандарта в Scala.

Например, в Java я люблю как можно скорее возвращаться из методов. Мы не любим много внутренних вложений. Если у вас их много, это называется антипаттерном стрелки. Вы можете прочитать об этом здесь: http://c2.com/cgi/wiki?ArrowAntiPattern

Пример Java:

boolean isValid(String input){
  if (input.trim().length() <1) return false //return early
  if (input.substring(0,3).equals("sth")) return false;

  //do some more checks for validity then return true

  return true
}

Если бы я написал это на Scala, мне пришлось бы использовать операторы совпадений, что привело бы к большим отступам. Имейте в виду, что это всего лишь фиктивный пример, в котором я пытаюсь передать только вложение и отступы Scala.

def isValid(input:String):  Boolean = {
  (input.trim.length < 1) match {
    case true => 
      input.substring(0,3) match {
        case sthCase if(sthCase=="sth") =>
          //do some more checks etc

          //eventually arrive at the "true" branch
               ... case valid => true

        case sthElse if (sthCase=="bla") => //some more code etc
        case _ => false
      }
    case _ => false
  }
}

Я знаю, что этот пример можно было бы написать так же, как я написал пример для Java, но если бы вместо того, чтобы возвращать истину или ложь, я хотел бы присвоить это значение переменной, например: val valid = (input.trim.length < 1) match ..., тогда это единственный способ сделать это, поскольку вы можете ' t переназначить val. Я мог бы использовать var вместо val, но тогда я бы использовал Java вместо Scala.

Повторяемый вопрос: Итак, вопрос в том, является ли антипаттерн стрелки способом делать что-то в Scala или я что-то упускаю? Есть ли лучший способ сделать в Scala то, что я только что написал (имея в виду, что я хочу, чтобы результат оператора сопоставления был назначен переменной val).


person Adrian    schedule 05.06.2013    source источник


Ответы (4)


Обратите внимание, что вы всегда можете использовать декомпозицию в Scala, если все части кода являются одной записью / одним выходом. Это работает, потому что вложенные функции в Scala могут обращаться к локальным переменным внешней функции. Например.:

def isValid(input: String): Boolean = {
  def testLength = input.trim.length >= 1
  def testSomething = input.substring(0, 3) == "sth"

  return testLength && testSomething
}

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

Также обратите внимание, что использование соответствия для проверки логических значений или сравнения чего-либо на равенство является излишним.

person Reimer Behrends    schedule 05.06.2013
comment
Это тоже не совсем новая техника. См. Никлаус Вирт "Разработка программ путем поэтапного уточнения". Вероятно, это основная причина того, почему все языки семейства языков Pascal имеют вложенные процедуры / функции. - person Reimer Behrends; 05.06.2013
comment
действительно, но это только начало. Я, вероятно, сделаю еще один пост, продолжающий этот вопрос, но немного более глубокий / продвинутый, чем несколько вложенных логических значений. еще раз спасибо - person Adrian; 05.06.2013
comment
Ключевое слово return можно и нужно опустить - person 0__; 06.06.2013
comment
В качестве стилистического предпочтения я предпочитаю делать return явным в конце длинных функций. Это не только упрощает визуальный поиск конца функции (при условии, что у вас есть подсветка синтаксиса), но также дает понять, что мы имеем дело с функцией, а не с процедурой, без необходимости проверять сигнатуру функции. Семантика одинакова в любом случае, поэтому объективно ни один из вариантов не лучше. - person Reimer Behrends; 06.06.2013
comment
Если вы опустите return, это сделает ваш источник доступным для поиска returns, который можно заменить чем-то другим. - person Beryllium; 06.06.2013

Ваш пример java можно легко перевести на if/else в scala без явного возврата и без каких-либо дополнительных уровней вложенности.

def isValid(input: String) = {
  if (input.trim().length() <1) false
  else if (input.substring(0,3).equals("sth")) false
  else true
}

if/else как и все в scala, является выражением и имеет возвращаемое значение, так что вы также можете использовать его для присваивания:

val isValid =
  if (input.trim().length() <1) false
  else if (input.substring(0,3).equals("sth")) false
  else true

Или в этом случае вы также можете просто написать

val isValid = (input.trim().length() >= 1) && (!input.substring(0,3).equals("sth"))
person drexin    schedule 05.06.2013
comment
Я понимаю, о чем вы говорите. Я должен был использовать не логический пример, чтобы проиллюстрировать свою точку зрения. - person Adrian; 05.06.2013

Если в функции слишком много return, функции отсутствуют:

def isValid(s: String): Boolean = {
  def isLengthValid = s.trim().length() > 0
  def shouldNotContainsSomething = s.substring(0, 3) != "sth"

  isLengthValid && shouldNotContainsSomething
}
person Beryllium    schedule 05.06.2013
comment
Нет необходимости использовать equals для сравнения строк в Scala. Оператор == автоматически использует метод equals. - person 0__; 06.06.2013
comment
Он был скопирован из примера Java - я просто добавил == false, чтобы сохранить условие. Так что это действительно должно быть !=. Я его обновил. - person Beryllium; 06.06.2013

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

Между прочим, существует также метод require, который обычно используется для отказа при проверке (но с исключением).

person om-nom-nom    schedule 05.06.2013