2012-09-06 11 views
5

Piszę serię odpowiedzi XML z zewnętrznego magazynu danych. Podczas których muszę przetestować istnienie węzła podrzędnego i - jeśli istnieje - przetestować jego wartość. Aby to osiągnąć Mam następujący kod:Scala XML: test na istnienie węzła i wartość

... 
    val properties = for { 
    val row <- root \\ "ResultDescription" 
    val cond:Boolean = checkDetectionNode(row) match { 
     case Some(nodeseq) => { 
      val txt = nodeseq.text.toLowerCase 
      if (txt contains "non-detect") 
      false 
      else 
      true 
     } 
     case None => true 
    } 
    if (cond) 
    val name = (row \ "CharacteristicName").text 
    if (charNameList.exists(s => s == name) == false) 
    } yield { 
    getObservedProperty(name) match { 
     case Some(property) => { 
      charNameList = name :: charNameList 
      property 
     } 
    } 
    } 
... 

checkDetectionNode jest zdefiniowany jako takie:

private def checkDetectionNode(row: scala.xml.NodeSeq) : Option[scala.xml.NodeSeq] = { 
    if ((row \ "ResultDetectionConditionText") != null) 
    Some[scala.xml.NodeSeq]((row \ "ResultDetectionConditionText")) 
    else 
    None 
} 

Powyższe wyniki kodu w nieokreślonym błędem „nielegalną początku prostego wyrażenia” na linii val name.... Szczerze mówiąc nie jestem programistą Scala, ani nawet programistą funkcjonalnym (zawsze był bardziej stronniczy od OO/imperatywu). Używam Scali tylko przez kilka dni i opieram większość tego, co wiem od Javy i operatorów lambda. Niestety, nie mam czasu, aby usiąść i naprawdę nauczyć się Scali, jakbym chciał. Terminy, zrób z nas wszystkich głupców.

Mam nadzieję, że ktoś może rzucić okiem i dać mi znać, jeśli coś jest nie tak (jak jestem pewien). Próbowałem ograniczyć wyświetlany kod, co mam nadzieję, jest istotne dla pytania. Daj mi znać, jeśli potrzebujesz dodatkowego kodu.

Dzięki

+0

Chyba powinienem wyjaśnić, że powyższe jest częścią zrozumienia dla oświadczenia. "If (cond)" ma na celu określenie wykonania bloku yield. Zmieniłem moją odpowiedź, aby opracować więcej na temat kodu. – Cowan

Odpowiedz

1

Plik xml jest tutaj rozproszony. Problem polega na tym, że jeśli (cond) na końcu nie działa jako strażnik, po prostu wygląda na to, że kompilator uważa, że ​​jest to początek nowej części "wtedy".

W poniższym przykładzie:

val l = List(1,2,3,4,5) 

val r = for { 
    i <- l 
     if (i > 2) 
    x <- Some(i * 2) 
    } yield x 

dostaniesz list (6,8,10), jak można się było spodziewać.

Korzystanie

val r = for { 
    val i <- l if (i > 2) 
    val x <- Some(i * 2) 
    } yield x 

powinno Ci ostrzeżenie amortyzację i

val r = for { 
    val i <- l 
    if (i > 2) 
    val x <- Some(i * 2) 
    } yield x 

dostaje

error: illegal start of simple expression 
    val x <- Some(i * 2) 

Wystarczy usunąć Val przed generator (Wzorzec1 '< -' Expr [Guard]) i możesz wznowić normalną usługę. Przepływa również nieco ładniej, bez znalezienia valsów w pętli for.

+0

To jest najlepsza odpowiedź bezpośrednio związana z pytaniem. Niestety, spowodowało to ponowne przemyślenie problemu i dokonanie znaczącego ponownego napisania. – Cowan

0

if (cond) powinny być przestrzegane przez wyrażenie. W Scali, if jest bardziej podobny do operatora trójskładnikowego w Javie: jest to wyrażenie, a nie instrukcja. Deklaracja zmiennych nie jest wyrażeniem (tak jak w Javie), więc nie można uzyskać wartości val w części if.

Szczerze, nie mogę zgadnąć, co chcesz osiągnąć, więc nie mogę zaproponować poprawnej składniowo alternatywy, która ma sens. Ale jeśli masz więcej logiki, który zależy od cond, można umieścić go w bloku:

if (cond) { 
    val name = ... 
    // do more here 
} 
+0

Dzięki za zwięzłą odpowiedź. Zapomniałem, że instrukcja if może działać jako operator warunkowy. Jednak w tym przypadku używam wyrażenia if jako części kompleksowej operacji, która - jak sądzę (mogłem bardzo dobrze się pomylić) zmienia ją z operatora trójskładnikowego na warunkowe wymaganie dotyczące rachunku zysków i strat. Poprawiłem moje pytanie powyżej, aby dodać więcej kodu. – Cowan

3

Przede wszystkim należy pamiętać, że (row \ "ResultDetectionConditionText") nie będzie null jeśli ma dzieci z tą nazwą istnieje, to będzie po prostu pusty NodeSeq (idiomatyczny kod Scala ma tendencję do unikania zwracania null, jak zapewne zauważyłeś). Twój aktualny kod zawsze zwróci Some, co prawdopodobnie nie jest tym, czego potrzebujesz. Zmiana twojego != null na .nonEmpty rozwiąże ten problem.

Następny, tutaj jest bardziej zwięzły sposób pisania warunkową logikę:

val cond = (row \ "ResultDetectionConditionText").exists(
    _.text.toLowerCase contains "non-detect" 
) 

Ten mówi: uzyskać NodeSeq który zawiera wszystkie dzieci nazwanych "Result...", jeśli one istnieją, a następnie sprawdzić, NodeSeq dla węzła zawierający tekst "non-detect". Logika nie jest dokładnie taka sama jak twoja, ponieważ sprawdzamy indywidualnie tekst węzłów, ale faktycznie pasuje to do tego, co domyślam się, że twoja intencja jest jeszcze lepsza - prawdopodobnie nie chcesz czegoś takiego, aby zdać test:

val row = <row> 
    <ResultDetectionConditionText>non-d</ResultDetectionConditionText> 
    <ResultDetectionConditionText>etect</ResultDetectionConditionText> 
</row> 

Ale będzie to w twojej obecnej wersji.

Ale to nie rozwiązuje problemu "illegal start of simple expression" - po prostu rozwiązuje logikę i obcina szesnaście linii kodu do trzech. Problem polega na tym, że musisz zdecydować, jaki powinien być Twój name, jeśli test, który właśnie skończyłeś, nie powiedzie się.Najbardziej oczywistym rozwiązaniem jest użyć Option:

val name = if (cond) Some((row \ "CharacteristicName").text) else None 

Ale zależności od tego jak używasz name, niektóre inne podejście może być bardziej odpowiednie.

+0

Dziękuję za odpowiedź i wskazując te problemy, zastosuję te zmiany do kodu. Zmieniłem moje pytanie, aby wskazać, że wszystko to jest kompleksowe, bo myślę, że (może być błędne) zmienia trójskładnikowy charakter wyrażenia if. – Cowan

Powiązane problemy