如何在Elixir中加入字符串?


158

如何在带有空格的列表中连接两个字符串,例如:

["StringA", "StringB"]

变成

"StringA StringB"

Answers:


220

如果您只想加入一些任意列表:

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

或仅使用字符串插值:

 "#{a} #{b}"

如果列表大小是任意的:

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

...以上所有解决方案将返回

"StringA StringB"

36
使用管道运算符的替代语法: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell 2013年

11
当您实际上不需要管道操作时,应避免使用管道操作员。
卡洛斯

3
@EdMelo Care详细说明了为什么?从技术上讲,您永远不需要真正地“需要”管道操作,因为可以通过嵌套函数调用来实现相同的行为。
Schrockwell '16

8
@Schrockwell是的,“应该”太多了。我的意思是,在这种情况下,您没有任何可读性,因此简单的函数调用将使思考更加明确。
卡洛斯

3
您应该尽可能多地使用Elixir语言,以便向潜在的雇主证明您知道这种语言。因此,我将在同一文件中使用上述所有解决方案。
rodmclaughlin

61

如果您拥有的是任意列表,则可以使用Enum.join,但是如果只有两个或三个,则更容易阅读显式字符串连接

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

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

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

由于您可以在某个位置将这些字符串作为变量使用深列表,因此可以避免分配整个新字符串以将其输出到其他地方。elixir / erlang中的许多功能都了解iolist,因此您通常不需要做额外的工作。


如果你需要在一个管道命令“字符串”的末尾添加东西|>(&(&1 <> “\ n”))()
hwatkins

9

为了完整起见,您还可以使用String插值

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

5

如果可以在列表中添加空格,可以将其视为iolist:

["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结尾)“ ab c”
Andrei Sura

我个人认为这是最好的答案,因为它可以推广到其他可以使用reduce的情况。说在河的“do.call”的理念
托马斯布朗

3

这取决于您要执行的操作。如果您只是想写出一个新变量,请使用以下任一方法:

  • 字符串插值

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • 字符串组合: "StringA" <> " " <> "StringB

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

但是,正如Uri所提到的,也可以使用IOLists:

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

如果您需要关心资源消耗,则IOLists实际上将是性能最高的。Big Nerd Ranch对于IOLists的性能提升有很好的记录


2

有很多方法,但是了解它如何处理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或类型错误时是否要崩溃。



0

考虑使用IO列表,如果您具有[“ String1”,“ string2”]并且在其上使用iolist_to_binary / 1,则将这些字符串复制到新字符串中。如果您有IO列表,则在大多数情况下都可以将其输出,并将其连接在端口上。这是关键,运行时不需要复制数据,因此它比串联更有效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.