2013-06-07 9 views
11

Mam trzy ciągi, na przykład "A", "B", "C". Muszę wytworzyć ciąg, który wynika z łączenia ich, tylko drugi ciąg musi być dopełniony białymi spacjami do określonej długości.Łączone struny wyściełane

To była moja pierwsza próba jak kierować intuicją i ogólnie Scala newbiness:

val s1 = "A" 
val s2 = "B" 
val s3 = "C" 
val padLength = 20 

val s = s1 + s2.padTo(padLength, " ") + s3 

co jest błędne, ponieważ padTo zwraca SeqLike którego toString nie zwraca ciąg wewnątrz, ale wektorze podobną reprezentację.

Jaki byłby najlepszy idiomatyczny sposób na zrobienie tego w Scali?

+0

+1 za intuicję + nowość. –

Odpowiedz

19

String może być (przez niejawna konwersja do StringOps tutaj) uznał jak zbiór znaków, więc wypełnienie powinno być:

val s = s1 + s2.padTo(padLength, ' ') + s3 // note the single quotes: a char 

Wywołanie .padTo(padLength, " ") na String faktycznie zwraca Seq[Any] od skończyć z obu znaków i ciągów w sekwencji.

2

to działa:

val s = s1 + s2.padTo(padLength, " ").mkString + s3 

ale jakoś czuje się słaby.

+0

Lepsze sformułowanie: b.padTo (10, "") .mkString (a, "", c) –

+0

@ som-snytt tak tak tak, czas slapface. –

8

Nie powiedziałeś, czy chcesz padać w lewo czy w prawo. Wystarczy użyć formatu:

val s = f"$s1%s$s2%20s$s3" 

Albo przed Scala 2,10 (lub jeśli trzeba „20” jako parametr):

val s = "%s%"+padLength+"s%s" format (s1, s2, s3) 

użyć wykluczających wyściółkę, aby dodać przestrzeń dopełnienie w prawo zamiast w lewo .

+1

Dla każdego, kto nie zareagował, wtyczka dla niebezpieczeństw dostosowywania głupiego, małego rutowania, http://scalapuzzlers.com/#pzzlr-027, oops podczas dodawania https://github.com/scala/scala /blob/master/src/compiler/scala/tools/nsc/Global.scala#L751. –

1
s1 + s2 + " "*(padLength - s2.size) + s3 
2

Dostępna jest metoda formatted. Na przykład:

val s = s1 + s2.formatted("%-10s") + s3 

jak format:

val s = s1 + "%-10s".format(s2) + s3 

ale chciałbym używać go tylko, jeśli padLength mogą być osadzone bezpośrednio.

Jeśli jest zdefiniowany gdzie indziej (jak w twoim przykładzie), możesz użyć padTo, tak jak wskazał gourlaysama.

Jeśli chcesz „pad w lewo”, można użyć niejawny Klasa:

object Container { 
    implicit class RichChar(c: Char) { 
    def repeat(n: Int): String = "".padTo(n, c) 
    } 

    implicit class RichString(s: String) { 
    def padRight(n: Int): String = { 
     s + ' '.repeat(n - s.length) 
    } 

    def padLeft(n: Int): String = { 
     ' '.repeat(n - s.length) + s 
    } 
    } 
} 

object StringFormat1Example extends App { 
    val s = "Hello" 

    import Container._ 

    println(s.padLeft(10)) 
    println(s.padRight(10)) 
} 
5

Ktoś powinien wspomnieć, że powinien pojawić się ostrzeżenia:

[email protected]:~$ skala -Ywarn-infer-any 
Welcome to Scala version 2.11.0-20130524-174214-08a368770c (OpenJDK 64-Bit Server VM, Java 1.7.0_21). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> "abc".padTo(10, "*").mkString 
<console>:7: warning: a type was inferred to be `Any`; this may indicate a programming error. 
     val res0 = 
     ^
res0: String = abc******* 

pamiętać, że nie ma nic złego (per se) z robieniem tego w ten sposób.

Może to przypadek użycia dla:

scala> case class Ikon(c: Char) { override def toString = c.toString } 
defined class Ikon 

scala> List(Ikon('#'),Ikon('@'),Ikon('!')).padTo(10, "*").mkString 
res1: String = #@!******* 

lub lepiej

scala> case class Result(i: Int) { override def toString = f"$i%03d" } 
defined class Result 

scala> List(Result(23),Result(666)).padTo(10, "---").mkString 
res4: String = 023666------------------------ 

Ponieważ to nie jest przypadek użycia, może należy zapytać, czy chcesz korzystać z API, które jest gadatliwy i najeżona niebezpieczeństwem.

Oto dlaczego odpowiedź Daniela jest prawidłowa. Nie jestem pewien, dlaczego ciąg znaków w jego przykładzie wygląda tak przerażająco, ale zazwyczaj wygląda na bardziej łagodny, ponieważ w większości czytelnych ciągów, potrzebujesz tylko znaków formatujących w kilku miejscach.

scala> val a,b,c = "xyz" 

scala> f"$a is followed by `$b%10s` and $c%.1s remaining" 
res6: String = xyz is followed by `  xyz` and x remaining 

Jeden przypadek, w którym jesteś zobowiązany do dodania fałszywych formatowania jest, gdy chcesz przełamane:

scala> f"$a%s%n$b$c" 
res8: String = 
xyz 
xyzxyz 

myślę interpolator powinien obsługiwać f "$ a% n $ b". Och, trzymaj się, zostało to naprawione w 2.11.

scala> f"$a%n$b" // old 
<console>:10: error: illegal conversion character 
       f"$a%n$b" 

scala> f"$a%n$b" // new 
res9: String = 
xyz 
xyz 

Więc teraz nie ma żadnej wymówki, aby nie interpolować.

+0

+1 za wyjaśnienia. Nie podoba mi się odpowiedź Daniela, że ​​interpolowany ciąg jest znacznie mniej czytelny niż opcja .padTo. –

1

Warto to dodanie do przeformułowań Różne:

scala> val a = "A"; val b="B";val c="C" 
a: String = A 
b: String = B 
c: String = C 

scala> b.padTo(10, " ").mkString(a, "", c) 
res1: String = AB   C 

który zapisuje niektóre + 's do kościoła w niedzielę.

Stałem się wierzącym w eksploatację mkString pod numerem this really wonky commit.