我想构建一个相对较长的字符串(如果重要的话是SVG)并从函数返回它。在Elixir中构建这样一个字符串的最佳方法是什么?在其他语言中,我会使用类似StringBuilder类的东西。 Elixir中有相同的东西吗?
您可以使用<>追加字符串运算符,但这不仅仅是一个列表附加?似乎做很多事情会变得非常低效。
答案 0 :(得分:7)
Elixir Strings(在Erlang中称为二进制)在内存中表示为连续的字节序列,而不是字节/字符的链接列表,如果这意味着你的意思是" list"。如果字符串的长度为O(n+m)
和n
,那么 是不可变的,附加两个二进制文件的简单实现将为m
,但Erlang VM会优化使用构建一个大字符串的情况:如果你有两个字符串a
和b
,a
在分配后有空闲内存,你将它们连接起来(a <> b
), VM仅复制b
并重用a
的旧值。如果您稍后将另一个字符串c
连接到a
,则此优化将无法应用,但此优化本身足以使构建大型二进制文件的任务与使用可变字符串的语言一样高效。详细解释了此优化here。
以下是此次优化的演示。在第一个示例中,我通过附加到基值来创建10,000,000字节的字符串。在第二个例子中,我通过预先添加到基值来创建500,000字节的字符串,这比追加10,000,000字节花费的时间多10倍。在一个天真的实现中,两者都需要相同的时间。
{time, _} = :timer.tc(fn ->
Enum.reduce(1..10_000_000, "", fn _, acc -> acc <> "." end)
end)
IO.inspect time
{time, _} = :timer.tc(fn ->
Enum.reduce(1..500_000, "", fn _, acc -> "." <> acc end)
end)
IO.inspect time
683621
7807815
简而言之,只要您只附加该值,就可以很好地构建大字符串。
如果您要将结果字符串写入套接字或流或类似字符串,则可以通过创建iolists而不是平面字符串来显着提高它。有关here和here的更多信息。
答案 1 :(得分:1)
Erlang和Elixir有另一种叫做IO lists的字符串,它们是由许多二进制文件(elixir中的字符串)和IO列表组成的列表,所以它是一个递归类型。
示例:
helloworld = ["Hello" | ["World"]]
sentence = ["A ", "small", " bike" | [" is", " red."]]
paragraph = [helloworld, ". ", sentence]
如您所见,我们将两个IO列表helloworld
和sentence
合并到另一个IO列表paragraph
。发生这种情况时没有复制或将任何数据附加到实际二进制文件,因为新列表paragraph
内部只包含其他列表和". "
字符串的地址位置。
这使您可以廉价地组合大量的二进制文件列表。很多Elixir和Erlang函数都可以使用IO列表,例如,Phoenix&#39; EEx模板返回IO列表,这些列表最终传递到套接字以发送出去,同时仍然是IO列表。
Poison的编码器也会在内部使用IO列表,因此不必为每个列表或对象编码的对象组合字符串。