2013-08-24 10 views
11

Mam metodę, która zwraca ciąg. Chcę zastąpić go wartością domyślną, taką jak "<empty>", jeśli zwraca pusty ciąg lub null. Załóżmy, że ma on nazwę getSomeString, jest to kosztowna operacja, więc mogę ją nazwać tylko raz i nie mogę zmienić jej typu zwrotu na Option[String]. Na razie wykonuję następujące czynności:Jak zastąpić pusty ciąg (lub pusty) za pomocą domyślnego ciągu tekstowego w Scala

val myStr = { 
    val s = getSomeString 
    if (s == null || s.isEmpty) "<empty>" else s 
} 

Czy istnieje prostszy sposób na osiągnięcie tego samego?

+0

Jeśli nie był to test isEmpty, można po prostu przejść do Option (getSomeString), aby przekonwertować go na Option [String]. Czy w praktyce otrzymujesz zarówno puste, jak i puste ciągi? –

+0

Tak, niestety. – trustin

Odpowiedz

12

Biorąc drogie funkcja:

scala> def s(i: Int): String = i match { case 0=>null case 1=>"" case 2=>"hi" } 
s: (i: Int)String 

myślę, że to jest łatwe do odczytania i wolny od napowietrznych, cf this in the wild:

scala> def q(i: Int) = s(i) match { case ""|null => "<empty>" case x => x } 
q: (i: Int)String 

scala> q(0) 
res3: String = <empty> 

scala> q(1) 
res4: String = <empty> 

scala> q(2) 
res5: String = hi 

Do moich oczach, to nie jest tak wyrazisty, nawet z minimalistycznym interpunkcyjne

scala> Option(s(0)) filterNot (_.isEmpty) getOrElse "<empty>" 
res6: String = <empty> 

Ponadto, kontrast koszt w anonfun zajęć dla zamknięć oraz dodatkowe wywołania metody:

scala> :javap - 
    Size 1161 bytes 
    MD5 checksum 765f5f67b0c574252b059c8adfab1cf0 
    Compiled from "<console>" 
[...] 
     9: getstatic  #26     // Field scala/Option$.MODULE$:Lscala/Option$; 
     12: getstatic  #31     // Field .MODULE$:L; 
     15: iconst_0  
     16: invokevirtual #35     // Method .s:(I)Ljava/lang/String; 
     19: invokevirtual #39     // Method scala/Option$.apply:(Ljava/lang/Object;)Lscala/Option; 
     22: new   #41     // class $anonfun$1 
     25: dup   
     26: invokespecial #42     // Method $anonfun$1."<init>":()V 
     29: invokevirtual #48     // Method scala/Option.filterNot:(Lscala/Function1;)Lscala/Option; 
     32: new   #50     // class $anonfun$2 
     35: dup   
     36: invokespecial #51     // Method $anonfun$2."<init>":()V 
     39: invokevirtual #55     // Method scala/Option.getOrElse:(Lscala/Function0;)Ljava/lang/Object; 
     42: checkcast  #57     // class java/lang/String 
     45: putfield  #17     // Field res6:Ljava/lang/String; 

Wzór Dopasowanie jest ogólnie po prostu jeśli, w przeciwnym razie, mniejsze i szybsze (nawet biorąc pod uwagę, że nie jest to optymalizacja s == "" do s.isEmpty):

scala> :javap -r #q 
    public java.lang.String q(int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=5, args_size=2 
     0: getstatic  #19     // Field $line3/$read$$iw$$iw$.MODULE$:L$line3/$read$$iw$$iw$; 
     3: iload_1  
     4: invokevirtual #22     // Method $line3/$read$$iw$$iw$.s:(I)Ljava/lang/String; 
     7: astore_3  
     8: ldc   #24     // String 
     10: aload_3  
     11: invokevirtual #28     // Method java/lang/Object.equals:(Ljava/lang/Object;)Z 
     14: ifeq   22 
     17: iconst_1  
     18: istore_2  
     19: goto   33 
     22: aload_3  
     23: ifnonnull  31 
     26: iconst_1  
     27: istore_2  
     28: goto   33 
     31: iconst_0  
     32: istore_2  
     33: iload_2  
     34: ifeq   44 
     37: ldc   #30     // String <empty> 
     39: astore  4 
     41: goto   47 
     44: aload_3  
     45: astore  4 
     47: aload   4 
     49: areturn  

Ale zainspirowana inną odpowiedzią, nawet jeśli nigdy nie zabrałabym tego kodu do domu, żeby spotkać się z rodzicami (ponieważ niepoprawnie przelicza wartość "null", jeśli funkcja ta zwraca je - chociaż może to jest funkcja do tego), tutaj jest regex:

scala> def p(i: Int) = "" + s(i) replaceAll ("^null$|^$", "<empty>") 
p: (i: Int)String 

"" + s(i) jest skrótem String.valueOf, co oczywiście daje String "null" o zerowej wartości odniesienia. Doceniam umiejętność SO nie tylko do generowania szybkich odpowiedzi na pytania, ale także do zachęcania do myślenia nieszablonowego.

+0

Javap utrudnia odczytanie twojego posta i zrozumienie twojego punktu. Co to jest tl; dr? Że jeśli jeszcze wzorzec dopasowania jest szybszy? – huynhjl

+0

@huynhjl Thx o opinie. Redagowałem tak, aby nie pochować prowadzenia. (Termin na gazetę, nie na metal). Nie, żebym oczekiwał, że zdobędę serca/umysły na SO. –

+0

@huynhjl W rzeczywistości, mówiąc silniej, jest to nie myślenia. Ale jeśli dzieciom wydaje się, że fajnie jest korzystać z opcji, to są gorsze rzeczy, które mogą robić, jak zamieszki na ulicach i powodować prawdziwy chaos. –

19
val myStr = Option(getSomeString).filterNot(_.isEmpty).getOrElse("<empty>") 

Updated

Zamieściłem ten kod, ponieważ myślę, że intencją w tym kodzie jest jasne niż if/else lub wzorzec wersja dopasowane, ale nie rozważenia kwestii wydajności.

Podobnie jak pozostałe w komentarzach, ten kod jest znacznie wolniejszy niż prosty, jeśli/else lub dopasowywanie do wzorca (ta linia utworzy wiele nowych obiektów, co jest kosztowną operacją), więc proszę nie używać tego kodu, gdy wydajność jest problem.

+1

Nie wygląda * znacznie * prościej w porównaniu z oryginalnym kodem (a ponadto ten fragment kodu będzie znacznie wolniejszy od poprzedniego). –

+1

Czuję, że jego partia jest czystsza. –

+1

To nie jest tak czyste i tanie jak (ahem) moja odpowiedź. Sprawdź ślad węglowy. –

1

Można zastąpić wartość null z pustym ciągiem w pierwszym etapie za pomocą Option a następnie podstawić z domyślnym tekstem jeśli wynik jest pusta (czy to dlatego, że był pusty pierwotnie lub dlatego, że był null):

Option(getSomeString).getOrElse("").replaceAll("^$","<empty>") 
2

Można dodać metodę String stosując klasę wartości niejawny

object ImplicitClassContainer { 
    implicit class RichString(val s: String) extends AnyVal { 
    def getOrDefault(defaultValue: String): String = { 
     s match { 
     case null | "" => defaultValue 
     case x => x 
     } 
    } 
    } 

być używany jak to

import ImplicitClassContainer._ 

println("hi".getOrDefault("<empty1>")) 

println("".getOrDefault("<empty2>")) 

val s: String = null 
println(s.getOrDefault("<empty3>")) 

więc nawet wywołanie metody na null jest obsługiwany wdzięcznie (Scala 2.10 .1).

+0

http://docs.scala-lang.org/overviews/core/implicit-classes.html – axaluss

+0

@axaluss Nie rozumiem Twojego komentarza: odpowiedź używa [implicit * value * classes] (http: //docs.scala -lang.org/sips/pending/value-classes.html): "Rozszerza AnyVal" – Beryllium

+1

Chciałem tylko dodać link do dokumentu. – axaluss

0
val myStr = getSomeString match { case ""|null => "<empty>" case s => s }