Як приєднати рядки до Elixir?


158

Як з'єднати два рядки у списку з пробілом, наприклад:

["StringA", "StringB"]

стає

"StringA StringB"

Відповіді:


220

Якщо ви просто хочете приєднатися до якогось довільного списку:

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

або просто використовувати рядкову інтерполяцію:

 "#{a} #{b}"

Якщо розмір списку довільний:

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

... всі рішення, наведені вище, повернуться

"StringA StringB"

36
Альтернативний синтаксис з використанням оператора конвеєра: ["StringA", "StringB"] |> Enum.join " "
Райан Кромвель

11
Ви повинні уникати оператора трубопроводу, коли у вас немає потреби в роботі трубопроводів.
Карлос

3
@EdMelo Care детальніше пояснити, чому? Технічно вам ніколи по-справжньому "не потрібно" виконувати операції з трубопроводом, оскільки така ж поведінка може бути досягнута за допомогою виклику функцій.
Шроквелл

8
@Schrockwell Так, "повинно" було занадто багато. Що я маю на увазі, що у цьому випадку ви не маєте переваги в читанні, тому звичайний виклик функції зробив би думки більш явними.
Карлос

3
Ви повинні використовувати якомога більше мови Elixir, щоб продемонструвати потенційним роботодавцям, що ви це знаєте. Тому я б використовував усі рішення, наведені вище, в одному файлі.
rodmclaughlin

61

Якщо у вас є довільний список, то ви можете використовувати Enum.join, але якщо це всього лише два-три, явне об'єднання рядків повинно бути легше читати

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

Однак часто вам не потрібно мати його як єдину струну в пам'яті, якщо ви збираєтеся виводити її через, наприклад, мережу. У цьому випадку може бути вигідним використання іолісту (конкретний тип глибокого списку), який позбавить вас від копіювання даних. Наприклад,

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

Оскільки ви б мали десь ці рядки як змінні, використовуючи глибокий список, ви уникаєте виділяти цілу нову рядок просто для виведення її в інше місце. Багато функцій в еліксирі / erlang розуміють іолісти, тому вам часто не потрібно буде зайву роботу.


Якщо вам потрібно додати щось наприкінці команди "String" |> (& (& 1 <> "\ n")). ()
hwatkins

9

Відповідаючи на повноту, ви також можете використовувати інтерполяцію String :

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

5

Якщо ви все в порядку з додаванням пробілу у своєму списку, ви можете трактувати його як іоліст:

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

Це дає змогу збільшити деякі виступи, оскільки ви не дублюєте жодну з рядків у пам'яті.


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" у Р.
Томас Браун

3

Це залежить від того, що ви намагаєтеся зробити. Якщо ви просто намагаєтесь виписати нову змінну, просто використовуйте будь-яку:

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

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

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

Однак, як згадував Урі, IOLists також можна використовувати:

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

IOLists насправді будуть найефективнішими, якщо вам потрібно дбати про споживання ресурсів. 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або неправильний тип.



0

Подумайте про використання списку вводу-виводу, якщо у вас є ["String1", "string2"] і ви використовуєте iolist_to_binary / 1 на ньому, тоді ви скопіюєте ці рядки в новий рядок. Якщо у вас є список IO, ви можете просто вивести його в більшості випадків, і він з'єднає його на порт. І це головне, час виконання не потрібно робити копії даних, тому це набагато ефективніше, ніж конкатенація.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.