Elixir - 如何每3个字符将字符串拆分为一个列表

Jos*_* An 13 elixir

如果我有字符串"UGGUGUUAUUAAUGGUUU"怎么把它变成一个列表,每3个字符分成一个["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]

Dog*_*ert 21

如果你的字符串只包含ASCII字符而你的字符串byte_size是3的倍数,那么使用一个鲜为人知的Elixir特性就有一个非常优雅的解决方案:二进制理解:

iex(1)> string = "UGGUGUUAUUAAUGGUUU"
"UGGUGUUAUUAAUGGUUU"
iex(2)> for <<x::binary-3 <- string>>, do: x
["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
Run Code Online (Sandbox Code Playgroud)

这会将字符串拆分为3个字节的块.这将比分割代码点或字形快得多,但如果您的字符串包含非ASCII字符,则无法正常工作.(在这种情况下,我会选择@ michalmuskala的回答.)

编辑:Patrick Oscity的回答提醒我,这也适用于代码点:

iex(1)> string = "????????????????????????"
"????????????????????????"
iex(2)> for <<a::utf8, b::utf8, c::utf8 <- string>>, do: <<a::utf8, b::utf8, c::utf8>>
["???", "???", "???", "???", "???", "???", "???", "???"]
Run Code Online (Sandbox Code Playgroud)

  • 通常,您希望在字形边界处拆分,而不仅仅是代码点.很多东西都可以使用,但是对于表情符号它会破坏.例如,``ébcde'`(首字母是两个代码点,e和组合急性),将成为`["éb","cde"]` (3认同)

Jos*_* An 13

"UGGUGUUAUUAAUGGUUU"
|> String.codepoints
|> Enum.chunk(3)
|> Enum.map(&Enum.join/1)
Run Code Online (Sandbox Code Playgroud)

我也想知道是否有更优雅的版本


mic*_*ala 9

这可以使用该Stream.unfold/2功能来实现.在某种程度上,它是相反的reduce- 减少允许我们将集合折叠成单个值,展开是将单个值扩展到集合中.

作为生成器,Stream.unfold/2我们需要一个返回元组的函数 - 第一个元素是生成集合的下一个成员,第二个元素是我们将要传递到下一个迭代的累加器.这准确描述了该功能String.split_at/2.最后,我们需要终止条件 - String.split_at("", 3)将返回{"", ""}.我们对空字符串不感兴趣,所以它应该足以处理我们生成的流,直到我们遇到空字符串 - 这可以通过实现Enum.take_while/2.

string
|> Stream.unfold(&String.split_at(&1, 3)) 
|> Enum.take_while(&(&1 != ""))
Run Code Online (Sandbox Code Playgroud)


Pat*_*ity 6

另一种可能性是使用Regex.scan/2:

iex> string = "abcdef"
iex> Regex.scan(~r/.{3}/, string)
[["abc"], ["def"]]

# In case the number of characters is not evenly divisible by 3
iex> string = "abcdefg"
iex> Regex.scan(~r/.{1,3}/, string)
[["abc"], ["def"], ["g"]]

# If you need to handle unicode characters, you can add the `u` modifier
iex> string = "abc"
iex> Regex.scan(~r/.{1,3}/u, string)
[[""], ["abc"]]
Run Code Online (Sandbox Code Playgroud)

或者使用递归函数,这有点冗长,但IMO应该是使用热切评估的最佳性能解决方案:

defmodule Split do
  def tripels(string), do: do_tripels(string, [])

  defp do_tripels(<<x::utf8, y::utf8, z::utf8, rest::binary>>, acc) do
    do_tripels(rest, [<<x::utf8, y::utf8, z::utf8>> | acc])
  end

  defp do_tripels(_rest, acc) do
    Enum.reverse(acc)
  end
end

#  in case you actually want the rest in the result, change the last clause to
defp do_tripels(rest, acc) do
  Enum.reverse([rest | acc])
end
Run Code Online (Sandbox Code Playgroud)


Hub*_*der 5

请试试

List.flatten(Regex.scan(~r/.../, "UGGUGUUAUUAAUGGUUU"))
Run Code Online (Sandbox Code Playgroud)

你会得到

["UGG", "UGU", "UAU", "UAA", "UGG", "UUU"]
Run Code Online (Sandbox Code Playgroud)

资料来源:

扫描方式

展平方法