2016-01-27 2 views
0

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

val first = Option(root.getA) 
    .flatMap(o => Option(o.getB)) 
    .flatMap(o => Option(o.getC)) 
... 
val second = Option(root.getA) 
    .flatMap(o => Option(o.getD)) 
    .flatMap(o => Option(o.getE)) 
... 
first.getOrElse(second.getOrElse... 

«первый» проверяет, является ли дерево имеет форму корне-> A-> B-> C ..., «второй» проверяет, является ли дерево имеет форму root-> A-> D-> E ... и т. д. Я чувствую, что должен быть более простой способ выразить эту идею в коде, поскольку весь этот код выполняет проверку нуля на каждом шаге путем упаковки и разворачивания параметров, но я не могу его найти.

ответ

1

If getA и т.д., заполнители для чего-то с параметрами, как getValue("A"), вы можете написать простую функцию которые приняли root и ("A", "B", "C") и легко прошли дерево, проверяя нулевые значения по пути. Я предполагаю, что вы задали вопрос, потому что это не так просто. Однако вы можете параметризовать по метод вызова с использованием отражения.

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

В качестве альтернативы, вы можете написать собственные экстракторы, если есть относительно несколько различных getX функции:

object GotA { 
    def unapply(x: Thing) = Option(x) map {_.getA} 
} 

Тогда ваш код становится простой шаблон сопоставление, как это:

root match { 
    case GotA(GotB(GotC(x))) => x 
    case GotA(GotD(GotE(x))) => x 
} 

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

val first = for { 
    a <- Option(root.getA) 
    b <- Option(a.getB) 
    c <- Option(b.getC) 
} yield c 

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

+0

Единственные реальные изменения Я сделал то, что я заменил такие вещи, как «CompilationUnit», «ClassDeclaration», «InterfaceDeclaration» и т. Д. A, B и C. (Узел-классы происходят из ANTLR генератора парсера.) –

1

Вы можете сделать:

val first = Try(root.getA.getB.getC).toOption 

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

val first = for { 
     a <- root.getA 
     b <- a.getB 
     c <- b.getC 
     ... 
+1

Использование 'Try' как, что это не только потенциально медленно, вероятно, неправильно: он ловит больше, чем просто' NullPointerException', а также ловит неработающими брошенные геттеры самих себя. – rightfold

+0

@MadameElyse Правильно. И когда я вижу «Попробуй», я полагаю, что «getA» что-то бросает, и никогда, причина этого «Try» в том, что 'getA' может вернуть значение null. Но я предложил это автору, потому что он хочет чего-то короче, чем у него, и я не думаю, что есть много других более коротких вариантов.Этот вариант крайне не рекомендуется использовать, и лучший, читаемый и простой способ - это понимание. – Archeg

+0

Существует причина, по которой 'Try' в scala не импортируется' Predef' - потому что его почти никогда не следует использовать – Archeg

0

Вы можете сделать это

case class Node(val a: Node, val b: Node, val c: Node, val data: Int) 

node match { 
    case Node(a: Node, b: Node, c: Node, _) => ... 
    case Node(null, b: Node, c: Node, _) => ... 
    case Node(a: Node, null, c: Node, _) => ... 
    case Node(a: Node, b: Node, null, _) => ... 
    case _ => ... 
} 
Смежные вопросы