Como juntar cordas no Elixir?


158

Como uno duas strings em uma lista com um espaço, como:

["StringA", "StringB"]

torna-se

"StringA StringB"

Respostas:


220

Se você quiser apenas entrar em uma lista arbitrária:

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

ou apenas use interpolação de string:

 "#{a} #{b}"

Se o tamanho da sua lista for arbitrário:

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

... todas as soluções acima retornarão

"StringA StringB"

36
A sintaxe alternativa usando o operador de pipeline: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell

11
Você deve evitar o operador do oleoduto quando não precisar de operações de tubulação.
Carlos

3
@ Edmelo Cuidado para explicar por quê? Tecnicamente, você nunca "realmente precisa" canalizar operações, pois o mesmo comportamento pode ser alcançado ao aninhar chamadas de função.
Schrockwell

8
@ Schrockwell sim, "deveria" era demais. O que quero dizer é que, neste caso, você não tem ganho em legibilidade, portanto, uma chamada de função simples tornaria os pensamentos mais explícitos.
Carlos

3
Você deve usar o máximo possível da linguagem Elixir para demonstrar aos possíveis empregadores que a conhece. Então, eu usaria todas as soluções acima no mesmo arquivo.
Rodmclaughlin

61

Se o que você tem é uma lista arbitrária, você pode usá-lo Enum.join, mas se for apenas por dois ou três, deve ser mais fácil ler a concatenação explícita de cadeias

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

No entanto, muitas vezes você não precisa tê-lo como uma única string na memória se quiser produzi-lo através, por exemplo, da rede. Nesse caso, pode ser vantajoso usar um iolist (um tipo específico de uma lista profunda), que evita a cópia de dados. Por exemplo,

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

Como você teria essas strings como variáveis ​​em algum lugar, usando uma lista profunda, evita alocar uma string totalmente nova apenas para produzi-la em outro lugar. Muitas funções no elixir / erlang entendem os iolistas, portanto, muitas vezes você não precisa fazer o trabalho extra.


Se você precisa adicionar algo no final de um comando pipe "String" |.> (& (& 1 <> "\ n")) ()
hwatkins

9

Respondendo à completude, você também pode usar a interpolação de String :

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

5

Se você não concordou em adicionar um espaço em sua lista, poderia tratá-lo como um iolist:

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

Isso dá a você algumas melhorias de desempenho, pois você não está duplicando nenhuma das strings na memória.


4

Um Enum.reduce também funcionaria para o seu exemplo, não?

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


Sim, mas precisa de um Enum.reduce reverso (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Andrei Sura

Pessoalmente, acho que essa é a melhor resposta, pois generaliza para outros casos em que reduzir pode ser usado. Fala da idéia de "do.call" em R.
Thomas Browne

3

Depende do que você está tentando fazer. Se você está apenas tentando gravar uma nova variável, use:

  • Interpolação de string

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Concatentação de string: "StringA" <> " " <> "StringB

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

No entanto, como Uri mencionou, as IOLists também podem ser usadas:

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

As LIOs realmente terão o melhor desempenho se você precisar se preocupar com o consumo de recursos. O Big Nerd Ranch tem uma boa descrição dos ganhos de desempenho com listas de usuários da Internet.


2

Existem vários métodos, mas saber como ele lida com valores nulos pode determinar qual método você deve escolher.

Isso gerará um erro

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)

Isto apenas irá inserir uma string "" em branco:

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

Como será isso:

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

Considere também os tipos. Com <>você, não há transmissão gratuita:

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"

O desempenho na prática parece aproximadamente o mesmo:

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}

Portanto, realmente depende se você deseja travar ou não quando os valores interpolados são nilou do tipo errado.


0

Você também pode fazer 'string A' ++ ' ' ++ 'string B'


7
Eles não se tornarão char-list?
Virtual

0

Considere usar uma lista de E / S, se você tiver ["String1", "string2"] e usar iolist_to_binary / 1 nela, copiará essas strings para uma nova string. Se você tiver uma lista de E / S, basta produzi-la na maioria dos casos e ela será concatenada na porta. E isso é a chave, o tempo de execução não precisará fazer cópias dos dados, portanto é muito mais eficiente que a concatenação.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.