Jak łączyć struny w Elixirze?


158

Jak połączyć dwa ciągi na liście spacją, na przykład:

["StringA", "StringB"]

staje się

"StringA StringB"

Odpowiedzi:


220

Jeśli chcesz tylko dołączyć do dowolnej listy:

"StringA" <> " " <> "StringB"

lub po prostu użyj interpolacji ciągów:

 "#{a} #{b}"

Jeśli rozmiar listy jest dowolny:

Enum.join(["StringA", "StringB"], " ")

... wszystkie powyższe rozwiązania powrócą

"StringA StringB"

36
Alternatywna składnia wykorzystująca operator potoku: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell

11
Należy unikać operatora rurociągu, gdy w rzeczywistości nie ma potrzeby wykonywania operacji na rurociągach.
Carlos

3
@EdMelo Care, aby wyjaśnić, dlaczego? Technicznie rzecz biorąc, nigdy tak naprawdę nie „potrzebujesz” operacji potokowych, ponieważ to samo zachowanie można osiągnąć przez zagnieżdżanie wywołań funkcji.
Schrockwell

8
@Schrockwell tak, „powinno” było za dużo. Chodzi mi o to, że w tym przypadku nie zyskujesz na czytelności, więc zwykłe wywołanie funkcji sprawiłoby, że myśli byłyby bardziej wyraźne.
Carlos

3
Powinieneś używać jak największej ilości języka Elixir, aby pokazać potencjalnym pracodawcom, że go znasz. Więc użyłbym wszystkich powyższych rozwiązań w tym samym pliku.
rodmclaughlin

61

Jeśli masz dowolną listę, możesz użyć Enum.join, ale jeśli jest to tylko dwie lub trzy, jawna konkatenacja ciągów powinna być łatwiejsza do odczytania

"StringA" <> " " <> "StringB"

Jednak często nie musisz mieć tego w pamięci jako pojedynczego ciągu, jeśli chcesz wyprowadzić go np. Przez sieć. W takim przypadku może być korzystne użycie iolist (szczególny rodzaj głębokiej listy), który chroni Cię przed kopiowaniem danych. Na przykład,

iex(1)> IO.puts(["StringA", " ", "StringB"])
StringA StringB
:ok

Ponieważ masz gdzieś te ciągi jako zmienne, używając głębokiej listy, unikasz przydzielania całego nowego ciągu tylko po to, aby wyprowadzić go gdzie indziej. Wiele funkcji w elixir / erlang obsługuje iolists, więc często nie trzeba wykonywać dodatkowej pracy.


Jeśli chcesz coś dodać na końcu polecenia potoku „String” |> (& (& 1 <> "\ n")). ()
hwatkins

9

Odpowiadając za kompletność, możesz również użyć interpolacji ciągów :

iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"

5

Jeśli nie przeszkadzało Ci dodanie spacji do listy, możesz traktować ją jako iolistę:

["StringA", " ", "StringB"] |> IO.iodata_to_binary # "StringA StringB"

Daje to pewne zwiększenie wydajności, ponieważ nie duplikujesz żadnego ze strun w pamięci.


4

Enum.reduce też by działało na twój przykład, nie?

iex(4)> Enum.reduce(["StringA", "StringB"], fn(x, acc) -> x <> " " <> acc end) "StringB StringA"


Tak, ale wymaga odwrotnego Enum.reduce (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c ”
Andrei Sura

Osobiście uważam, że jest to najlepsza odpowiedź, ponieważ uogólnia ona inne przypadki, w których można zastosować metodę redukcji. Mówi o idei „do.call” w R.
Thomas Browne

3

To zależy od tego, co próbujesz zrobić. Jeśli po prostu próbujesz napisać do nowej zmiennej, po prostu użyj:

  • Interpolacja ciągów

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Konkatentacja ciągów: "StringA" <> " " <> "StringB

  • Enum.join(): ["StringA", "StringB"] |> Enum.join(" ")

Jednak, jak wspomniał Uri, IOLists mogą być również używane:

["StringA", " ", "StringB"] |> IO.iodata_to_binary

IOList faktycznie będą najbardziej wydajne, jeśli musisz dbać o zużycie zasobów. Big Nerd Ranch ma dobry opis wzrostu wydajności z IOListami.


2

Istnieje wiele metod, ale wiedza o tym, jak obsługuje ona wartości zerowe, może określić, którą metodę należy wybrać.

Spowoduje to wyświetlenie błędu

iex(4)> "my name is " <> "adam"
"my name is adam"

iex(1)> "my name is " <> nil
** (ArgumentError) expected binary argument in <> operator but got: nil
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:1: (file)

Spowoduje to wstawienie pustego ciągu „”:

iex(1)> "my name is #{nil}"
"my name is "

Podobnie jak to:

iex(3)> Enum.join(["my name is", nil], " ")
"my name is "

Weź również pod uwagę typy. Z <>tobą nie dostaniesz żadnego darmowego odlewania:

iex(5)> "my name is " <> 1
** (ArgumentError) expected binary argument in <> operator but got: 1
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:5: (file)

iex(5)> "my name is #{1}"
"my name is 1"

iex(7)> Enum.join(["my name is", 1], " ")
"my name is 1"

Wydajność w praktyce wydaje się mniej więcej taka sama:

iex(22)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8023855, :ok}
iex(23)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8528052, :ok}
iex(24)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{7778532, :ok}
iex(25)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7620582, :ok}
iex(26)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7782710, :ok}
iex(27)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7743727, :ok}

Tak naprawdę zależy od tego, czy chcesz się zawiesić, czy nie, gdy interpolowane wartości są nillub są niewłaściwego typu.



0

Rozważ użycie listy we / wy, jeśli masz ["String1", "string2"] i używasz na niej iolist_to_binary / 1, skopiujesz te ciągi do nowego łańcucha. Jeśli masz listę IO, możesz ją po prostu wyprowadzić w większości przypadków, a ona połączy ją na porcie. I to jest kluczowa rzecz, środowisko wykonawcze nie będzie musiało kopiować danych, więc jest znacznie bardziej wydajne niż konkatenacja.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.