2015-10-12 15 views
10

Jestem nowy Scala i widziałem kod łączenie ciągów w Scala tak:Scala: String "+" vs "++"

"test " ++ "1" 

A ja testowałem, i to jest również napisane w Scala Doc

"test " + "1" 

Więc moje rozumienie jest + jest jak Java String + ale ++ jest mocniejszy, może wziąć w kilku rodzajów parametrów. Również ++ wydaje się być uniwersalny dla innych rzeczy, takich jak Lista. Chcę wiedzieć, czy moje zrozumienie jest poprawne. i jakiekolwiek inne różnice? Kiedy powinien jeden nad drugim tylko dla połączenia strun?

+0

Poza odpowiedziami, polecam, aby nie używać '+' dłużej. Najprawdopodobniej zostanie wycofany i ostatecznie usunięty. Teraz Scala ma interpolację ciągów, która jest preferowanym sposobem komponowania ciągów. Używanie '++' może być wolniejsze, ale myślę, że jest to dobra alternatywa, syntaktycznie czysta i zgodna z kolekcjami Scali. –

+0

@ 0__ 2 godziny temu, + jest z ciągu znaków Java prawda? Chodzi mi o to, czy Scala zachowałaby go dla ciągów Scala? –

+0

'+' nie jest zdefiniowane w API 'java.lang.String', w Javie jest to po prostu syntaktyczny cukier do uruchamiania kreatora stringów. To samo dotyczy Scala, gdzie łańcuchowanie z '+' jest definiowane przez kompilator nawet w przypadku braku 'Predef.any2stringadd'. Zobacz także: https://stackoverflow.com/questions/2664274/how-to-unimport-string-operator-in-scala –

Odpowiedz

10

Pomaga spojrzeć w scala.Predef aby zobaczyć, co dokładnie się dzieje.

Jeśli to zrobisz, zobaczysz, że String w Scali to tylko alias dla java.lang.String.Innymi słowy, metoda + na String zostaje przetłumaczona na operatora Javy +.

Tak więc, jeśli Scala String to tylko Java String, jak może istnieć metoda ++, możesz zapytać. (Cóż, chciałbym zapytać, przynajmniej.) Odpowiedź jest taka, że ​​istnieje niejawna konwersja z String do WrappedString dostarczona przez metodę wrapString, która jest również w Predef.

Zauważ, że ++ pobiera dowolną instancję GenTraversableOnce i dodaje wszystkie elementy w tej instancji do oryginalnego WrappedString. (Zauważ, że dokumenty niepoprawnie informują, że metoda zwraca wartość WrappedString[B]. To musi być niepoprawne, ponieważ WrappedString nie przyjmuje parametrów typu.) To, co otrzymasz, to albo String (jeśli rzecz, którą dodasz, to Seq[Char]) lub trochę IndexedSeq[Any] (jeśli nie jest).

Oto kilka przykładów:

Jeśli dodać String Do List[Char], masz String.

scala> "a" ++ List('b', 'c', 'd') 
res0: String = abcd 

Jeśli dodać String Do List[String], masz IndexedSeq[Any]. W rzeczywistości pierwsze dwa elementy to Char s, ale ostatnie trzy to String s, jak pokazuje to kolejne połączenie.

scala> "ab" ++ List("c", "d", "e") 
res0: scala.collection.immutable.IndexedSeq[Any] = Vector(a, b, c, d, e) 

scala> res0 map ((x: Any) => x.getClass.getSimpleName) 
res1: scala.collection.immutable.IndexedSeq[String] = Vector(Character, Character, String, String, String) 

Wreszcie, jeśli dodać do StringString z ++, wrócisz do String. Powodem tego jest fakt, że WrappedString dziedziczy po IndexedSeq[Char], więc jest to zawiły sposób dodawania Seq[Char] do Seq[Char], który daje ci powrót do Seq[Char].

scala> "abc" + "def" 
res0: String = abcdef 

Jak zauważył Alexey, żadna z nich nie jest bardzo subtelne narzędzie, więc jesteś prawdopodobnie lepiej wyłączyć za pomocą string interpolation lub StringBuilder chyba nie jest dobry powód, aby nie.

2

Więc moje zrozumienie jest, że + jest jak Java String + ++, ale jest mocniejszy, może wziąć w kilku rodzajów parametrów

Chodzi o to, + na Strings jest bardziej wydajny w tym sens: w ogóle może przyjmować dowolne parametry, tak jak w Javie. Często jest to uważane za błąd (zwłaszcza, że ​​działa również z ciągami po prawej), ale jesteśmy z tym mocno związani. ++ to, jak mówisz, ogólna metoda zbierania i bardziej bezpieczna dla typu ("test " ++ 1 nie będzie się kompilować).

Kiedy należy się nawzajem po prostu do łączenia strun?

Wolałbym +. Jednak dla wielu (powiedziałbym nawet, że większość) zastosowań, których potrzebujesz, nie jest: użyj zamiast tego string interpolation.

val n = 1 
s"test $n" 

I oczywiście, kiedy buduje ciąg z wielu części, należy użyć StringBuilder.

+0

Masz rację, że "+" jest silniejsze, ale chcę tylko potwierdzić, że niejawna konwersja z int do string wydarzyło się w Javie, prawda? –

+0

Ściśle mówiąc, nie ma tu żadnej niejawnej konwersji. W języku Java dzieje się tak zwana konwersja ciągów znaków (https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.1); w Scala 'String' jest uważany za posiadający metodę' def + (other: Any) ', więc może przyjąć dowolny argument. Konwersja niejawna ma miejsce tylko wtedy, gdy wartość inna niż "String" bez odpowiedniej metody "+" znajduje się po lewej stronie i "String" po prawej stronie (patrz ['any2stringAdd'] (http://www.scala-lang.org/ api/current/index.html # scala.Predef $$ any2stringadd)), w tym przypadku dzieje się to w Scali. –

0

++ niekoniecznie jest "mocniejszy", ale jest ogólnie używany jako operacja konkatenacji/dołączania. Jednak nie wykonuje przypisania. IE listX ++ y dopisze do listy X, ale i++ nie będzie zwiększać liczby całkowitej i (ponieważ jest to przypisanie zmiennej w stosunku do mutacji).

To przynajmniej moje zrozumienie. Nie jestem ekspertem od Scala.

0

Istnieje niejawna konwersja z String na StringOps w scala.Predef. Metoda ++ jest zdefiniowana w klasie StringOps. Tak więc zawsze, gdy robisz str1 ++ str2, kompilator scala jest zasadniczo (z perspektywy kodera) opakowujący str1 w StringOps i wywołujący metodę z StringOps. Zauważ, że StringOps jest zasadniczo rodzajem IndexedSeq, więc operator ++ jest bardzo elastyczny, np.

"Hello, " ++ "world!" //results in "Hello, world" with type String 
"three" ++ (1 to 3) //results in Vector('t', 'h', 'r', 'e', 'e', 1, 2, 3) with type IndexedSeq[AnyVal] 
3

String to TraversableLike, co oznacza, że ​​można go rozłożyć na sekwencję elementów (znaków). Stąd pochodzi ++, w przeciwnym razie nie można wykonać ++ w String. ++ działałby tylko wtedy, gdy jego prawa strona (lub parametr do tej funkcji) jest typu składalnego (lub możliwego do przejścia).

Teraz, jak String staje się TraversableLike? To tam wchodzą implicity zdefiniowane w Predef.Jednym z niejawne zamienia normalny String w WrappedString gdzie WrappedString.canBuildFrom posiada wszystkie kleju, które zasadniczo działa w ten sposób:

WrappedString.canBuildFrom ->StringBuilder ->StringLike ->IndexedSeqOptimized ->IndexedSeqLike ->SeqLike ->IterableLike ->TraversableLike

Ponieważ implicits zdefiniowane w Szablony są już w zasięgu, możliwe jest napisanie kodu tak:

"test " ++ "1" 

teraz pytania:

Chcę wiedzieć, czy moje zrozumienie jest prawidłowe. i jakiekolwiek inne różnice?

Tak, twoje zrozumienie jest we właściwym kierunku.

Kiedy należy się nawzajem po prostu do łączenia strun?

W przypadku konkatenacji ciągów, wyraźnie "test " + "1" tworzy mniej obiektów i mniejszą liczbę wywołań funkcji. Jednak zawsze wolałbym interpolację ciągów jak:

val t1 = "test" 
val t2 = "1" 
val t3 = s"$t1 $t2" 

, która jest bardziej czytelna.

Aby uzyskać więcej informacji: