Как соединить струны в эликсире?

158

Как мне объединить две строки в списке с пробелом, например:

["StringA", "StringB"]

становится

"StringA StringB"
thiagofm
источник

Ответы:

220

Если вы просто хотите присоединиться к произвольному списку:

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

или просто используйте интерполяцию строк:

 "#{a} #{b}"

Если размер вашего списка произвольный:

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

... все решения выше вернутся

"StringA StringB"
thiagofm
источник
36
Альтернативный синтаксис с использованием оператора конвейера: ["StringA", "StringB"] |> Enum.join " "
Райан Кромвель
11
Вы должны избегать оператора трубопровода, когда у вас нет необходимости выполнять операции с конвейером.
Карлос
3
@EdMelo Хотите уточнить, почему? С технической точки зрения вам никогда не нужно «конвейерно» выполнять операции, так как такое же поведение может быть достигнуто с помощью вложенных вызовов функций.
Шроквелл
8
@ Schrockwell да, "должен" было слишком много. Я имею в виду, что в этом случае у вас нет улучшения читабельности, поэтому простой вызов функции сделает мысли более явными.
Карлос
3
Вы должны использовать как можно больше языка эликсира, чтобы продемонстрировать потенциальным работодателям, что вы его знаете. Поэтому я бы использовал все решения выше в одном файле.
Rodmclaughlin
61

Если то, что у вас есть, является произвольным списком, то вы можете использовать Enum.join, но если это всего лишь два или три, явное сцепление строк должно быть легче для чтения

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

Тем не менее, часто вам не нужно иметь его как одну строку в памяти, если вы собираетесь выводить ее, например, через сеть. В этом случае может быть полезно использовать iolist (определенный тип глубокого списка), который избавляет вас от копирования данных. Например,

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

Поскольку вы будете использовать эти строки в качестве переменных где-либо, используя глубокий список, вы избегаете выделения целой новой строки просто для вывода ее в другом месте. Многие функции в эликсире / эрланге понимают иолисты, поэтому вам часто не нужно выполнять дополнительную работу.

Карлос Мартин Ньето
источник
Если вам нужно что-то добавить в конце канала, команда «String» |> (& (& 1 <> "\ n")). ()
hwatkins
9

Отвечая на полноту, вы также можете использовать строковую интерполяцию :

iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"
Sheharyar
источник
5

Если у вас все в порядке с добавлением пробела в ваш список, вы можете рассматривать его как iolist:

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

Это дает вам некоторые улучшения производительности, поскольку вы не дублируете ни одну из строк в памяти.

Uri
источник
4

Enum.reduce тоже подойдет для вашего примера нет?

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

Низкий Киан Сон
источник
Да, но для этого нужен обратный Enum.reduce (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Андрей Сура
лично я думаю, что это лучший ответ, потому что он обобщает другие случаи, когда можно использовать сокращение. Говорит с идеей «do.call» в R.
Томас Браун
3

Это зависит от того, что вы пытаетесь сделать. Если вы просто пытаетесь записать новую переменную, просто используйте либо:

  • Строковая интерполяция

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Конкатенация строк: "StringA" <> " " <> "StringB

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

Однако, как упоминал Ури, списки IOL также могут быть использованы:

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

Списки IOL на самом деле будут наиболее эффективными, если вам нужно заботиться о потреблении ресурсов. Big Nerd Ranch имеет хорошую статью о повышении производительности с IOLists.

Джейсон Стейнхаузер
источник
2

Существует несколько методов, но знание того, как он обрабатывает нулевые значения, может определить, какой метод вы должны выбрать.

Это выдаст ошибку

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)

Это просто вставит пустую строку "":

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

Как будет это:

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

Также рассмотрите типы. С <>вами не получите ни одного бесплатного кастинга:

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"

Производительность на практике кажется примерно одинаковой:

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}

Итак, действительно зависит от того, хотите ли вы потерпеть крах или нет, когда интерполированные значения имеют nilнеправильный тип.

atomkirk
источник
0

Вы могли бы также сделать 'string A' ++ ' ' ++ 'string B'

ватсала
источник
7
Не станут ли они чар-листом?
Виртуальный
0

Попробуйте использовать IO List, если у вас есть ["String1", "string2"] и вы используете в нем iolist_to_binary / 1, тогда вы скопируете эти строки в новую строку. Если у вас есть список ввода-вывода, вы можете просто вывести его в большинстве случаев, и он объединит его с портом. И это главное, во время выполнения не нужно будет делать копии данных, поэтому это намного эффективнее, чем конкатенация.

Захари К
источник