Как написать этот метод в Scala или как лучше всего справиться с ранними возвращениями

Играя с немного Scala, и кажется, что вы не можете вернуться раньше от функции.

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

}
 }
}

Каков наилучший способ написать следующий метод в Scala?

Можно ли запрограммировать таким образом, чтобы уменьшить вложенность, или я буду бороться с концепцией функционального дизайна Scala в целом?

@Nullable
public static <t extends="" psielement=""> T getParentOfType(@Nullable PsiElement element,
 @NotNull Class<t> aClass,
 boolean strict,
 @NotNull Class<!--? extends PsiElement-->... stopAt) {
 if (element == null) return null;
 if (strict) {
 element = element.getParent();
 }

 while (element != null && !aClass.isInstance(element)) {
 if (instanceOf(element, stopAt)) return null;
 if (element instanceof PsiFile) return null;
 element = element.getParent();
 }

 //noinspection unchecked
 return (T)element;
}
</t></t>

Моя попытка:

def getParentOfType[T](element: PsiElement, aClass: Class, strict: Boolean): T[_ <: PsiElement] = {
 element match {
 case null => null
 case _ => {
 var el = if (strict) {
 element.getParent
 } else element
 while(el != null) {
 if (aClass isInstance el) {
 return el
 }
 if (el.isInstanceOf[PsiFile]) return null;
 el = el.getParent()
 }
 }
 }
}

Кроме того, что является подходящим форумом в Scala-мире, чтобы спросить: "Каков наилучший способ написать Java-метод X в Scala?". Я очень много задаю этот вопрос.

3 ответа

def getParentOfType[T](element: PsiElement, aClass: Class, strict: Boolean):
 T[_ <: PsiElement] = element match {
 case null => null
 case el if strict => getParentOfType(el.getParent, aClass, false)
 case el if aClass isInstance el => el
 case el if el.isInstanceOf[PsiFile] => null
 case el => getParentOfType(el.getParent, aClass, false)
}

(или похожие)


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

def getParentOfType[A >: Null <: PsiElement](
 element: PsiElement, aClass: Class[A],
 strict: Boolean, stopAt: Class[_ <: PsiElement]*
): A =
 Iterator.iterate(element)(_.getParent).
 takeWhile(_ != null).
 drop(if (strict) 1 else 0).
 takeWhile(e => !instanceOf(e, stopAt) && !e.isInstanceOf[PsiFile]).
 collectFirst{ case x if aClass isInstance x => x.asInstanceOf[A] }.
 orNull

Здесь вы начинаете с определения потока родителей вашего элемента, который заканчивается, если он равен нулю. Вы бросаете первый, если ваш строгий флаг истинен; вы также прекратите поиск, если вы нажмете что-то в stopAt или PsiFile. В пределах этих ограничений вы получаете (и бросаете) первое, что соответствует, а затем вы возвращаете это или null, если вы ничего не получили.

С небольшой практикой на самом деле может быть проще следовать такой логике, кроме цикла, поскольку условия завершения более неявны в цикле, чем здесь. Здесь вы просто указываете, что вы прекратите поиск (takeWhile) и то, что вам нужно (collectFirst).

Примечание. Я предполагаю, что instanceOf определяется как

def instanceOf(a: AnyRef, cs: Seq[Class[_]]) = cs.exists(_ isInstance a)


Трудно использовать scala с null. И вы не можете получить идиоматический код SCALA с while.

У вас @Nullable и @NotNull - в scala это Option[T] и T Вы можете заменить while рекурсивный метод.

Например, это моя попытка перевести ваш метод на scala (не проверен):

getParentOfType[T <: PsiElement](element: Option[PsiElement],
 aClass: Class[T],
 strict: Boolean,
 stopAt: Class[_ <: PsiElement]*): Option[T] = element flatMap { el =>
 @tailrec def loop(element: PsiElement): Option[PsiElement] {
 if (element == null || aClass.isInstance(element))
 Option(element)
 else if (instanceOf(element, stopAt) || element.isInstanceOf[PsiFile])
 None
 else
 loop(element.getParent())
 }

 loop(if (strict) el.getParent() else el).map{_.asInstanceOf[T]}
}

licensed under cc by-sa 3.0 with attribution.