如何在长生不老药中连接弦?

如何用空格连接列表中的两个字符串,如:

["StringA", "StringB"]

变成了

"StringA StringB"
103366 次浏览

如果你只是想加入一些随意的列表:

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

或者仅仅使用字符串插值:

 "#{a} #{b}"

如果您的列表大小是任意的:

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

所有的解决方案都会回来

"StringA StringB"

如果您有一个任意的列表,那么您可以使用 Enum.join,但是如果它只用于两个或三个,显式字符串串联应该更容易阅读

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

但是,如果要通过网络等方式输出,通常不需要将它作为单个字符串存储在内存中。在这种情况下,使用 iolist (深度列表的特定类型)可能是有利的,这样可以避免复制数据。比如说,

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

因为您可以将这些字符串作为变量放在某个地方,所以通过使用深度列表,您可以避免仅仅为了在其他地方输出而分配一个全新的字符串。Elixir/erlang 中的许多功能都能理解 iolist,因此您通常不需要做额外的工作。

Enum.reduce 也适合您的示例,不是吗?

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

你也可以做 'string A' ++ ' ' ++ 'string B'

如果你可以在你的列表中添加一个空格,你可以把它当作 iolist:

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

由于不复制内存中的任何字符串,因此可以提高性能。

答案是完整的,你也可以使用 字符串插值:

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

这取决于你想做什么。如果您只是想写出一个新变量,那么只需要使用:

  • 字符串插值

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • String concatentation: "StringA" <> " " <> "StringB

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

However, as Uri mentioned, IOLists can also be used:

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

IOLists are actually going to be the most performant if you need to care about resource consumption. Big Nerd Ranch has a good write-up on performance gains w/ IOLists.

考虑使用 IO 列表,如果您有[“ String1”,“ string2”] ,并在其上使用 iolist _ to _ binary/1,那么您将把这些字符串复制到一个新的字符串中。如果您有一个 IO 列表,那么在大多数情况下您只需要输出它,它就会将它连接到端口上。这是关键所在,运行时不需要复制数据,所以它比串联更有效。

每个方法如何处理 nil

有许多方法,但是了解它如何处理 nil 值可以决定您应该选择哪个方法。

这将抛出一个错误

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或错误的类型。