Come unire le stringhe nell'elisir?


158

Come posso unire due stringhe in un elenco con uno spazio, come:

["StringA", "StringB"]

diventa

"StringA StringB"

Risposte:


220

Se vuoi solo unirti ad un elenco arbitrario:

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

o usa semplicemente l'interpolazione di stringhe:

 "#{a} #{b}"

Se la dimensione dell'elenco è arbitraria:

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

... tutte le soluzioni sopra riportate torneranno

"StringA StringB"

36
La sintassi alternativa che utilizza l'operatore pipeline: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell il

11
È necessario evitare l'operatore della pipeline quando in realtà non è necessario eseguire le tubazioni.
Carlos,

3
@EdMelo Care per approfondire il perché? Tecnicamente non è mai veramente "necessario" eseguire le operazioni di pipe, poiché lo stesso comportamento potrebbe essere ottenuto annidando le chiamate di funzione.
Schrockwell,

8
@Schrockwell sì, "dovrebbe" era troppo. Quello che voglio dire è che in questo caso non hai alcun guadagno in leggibilità, quindi una semplice chiamata di funzione renderebbe le considerazioni più esplicite.
Carlos,

3
Dovresti usare tutta la lingua di elisir possibile per dimostrare ai potenziali datori di lavoro che la conosci. Quindi userei tutte le soluzioni sopra nello stesso file.
rodmclaughlin,

61

Se quello che hai è un elenco arbitrario, allora puoi usare Enum.join, ma se è solo per due o tre, la concatenazione di stringhe esplicite dovrebbe essere più facile da leggere

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

Tuttavia, spesso non è necessario averlo come singola stringa in memoria se lo si desidera generare attraverso, ad esempio, la rete. In tal caso, può essere vantaggioso utilizzare una iolist (un tipo specifico di un elenco approfondito), che consente di risparmiare dalla copia dei dati. Per esempio,

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

Dal momento che dovresti avere quelle stringhe come variabili da qualche parte, usando un elenco profondo, eviti di allocare una stringa completamente nuova solo per produrla altrove. Molte funzioni di elisir / erlang comprendono gli iolisti, quindi spesso non dovresti fare il lavoro extra.


Se devi aggiungere qualcosa alla fine di un comando pipe "String" |> (& (& 1 <> "\ n")). ()
hwatkins

9

Rispondendo per completezza, puoi anche usare l' interpolazione di stringhe :

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

5

Se stavi bene con l'aggiunta di uno spazio nel tuo elenco, potresti trattarlo come un iolista:

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

Questo ti dà alcune prestazioni in quanto non stai duplicando nessuna delle stringhe in memoria.


4

Un Enum.reduce funzionerebbe anche per il tuo esempio no?

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


Sì, ma ha bisogno di un Enum.reduce inverso (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Andrei Sura,

personalmente penso che questa sia la risposta migliore perché si generalizza ad altri casi in cui è possibile utilizzare la riduzione. Parla dell'idea di "do.call" in R.
Thomas Browne,

3

Dipende da cosa stai cercando di fare. Se stai solo cercando di scrivere su una nuova variabile, usa semplicemente:

  • Interpolazione di stringhe

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Concatenazione di stringhe: "StringA" <> " " <> "StringB

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

Tuttavia, come menzionato da Uri, gli IOList possono anche essere usati:

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

Gli IOList saranno effettivamente i più performanti se hai bisogno di preoccuparti del consumo di risorse. Big Nerd Ranch ha una buona descrizione degli aumenti delle prestazioni con IOLists.


2

Esistono diversi metodi, ma sapere come gestisce valori nulli può determinare quale metodo scegliere.

Questo genererà un errore

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)

Questo inserirà semplicemente una "" stringa vuota:

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

Come sarà questo:

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

Considera anche i tipi. Con <>te non ricevi nessun casting gratuito:

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"

Le prestazioni in pratica sembrano all'incirca le stesse:

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}

Quindi, dipende davvero se vuoi andare in crash o meno quando i valori interpolati sono nilo il tipo sbagliato.


0

Potresti anche fare 'string A' ++ ' ' ++ 'string B'


7
Non diventeranno char-list?
Virtual

0

Prendi in considerazione l'utilizzo di un Elenco IO, se hai ["String1", "string2"] e usi iolist_to_binary / 1 su di esso, copierai quelle stringhe in una nuova stringa. Se si dispone di un elenco IO, è possibile emetterlo nella maggior parte dei casi e lo concatenerà sulla porta. E questa è la cosa chiave, il runtime non avrà bisogno di fare copie dei dati, quindi è molto più efficiente della concatenazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.