将字符串拆分成多个chunk,每个chunk不超过100个字符,拆分不能在数字中间
split the string in multiple chunks, with each chunk not exceeding 100 characters and the split cannot be in the middle of a number
给定一个整数枚举(来自外部源),我需要将它们转换为字符串,然后加入它们(由 space 分隔)。
第二部分是将结果字符串拆分成多个chunk,每个chunk不超过100个字符,拆分不能在数字中间(所以拆分只能发生在插入数字之间的 space 上。
第一部分很简单。实际上将生成的字符串的大小保持在限制之下是一个挑战。
查看了 Enum。chunk_while/4 但不确定这是正确的方法。我还探索了在列表为整数时拆分列表并尝试加入结果但没有运气。
这是一个函数,join_len/2
,一次完成。它的工作原理是构建每个字符串,直到达到 100 个字符,然后移动到下一个字符串。
defmodule Example do
def join_len(enum, len), do: join_len(enum, "", [], len)
defp join_len([], curr, done, _len), do: Enum.reverse([curr | done])
defp join_len([num | rest], curr, done, len) do
str = Integer.to_string(num)
size = byte_size(str)
curr_size = byte_size(curr)
cond do
curr_size == 0 -> join_len(rest, str, done, len)
curr_size + size + 1 <= len -> join_len(rest, "#{curr} #{str}", done, len)
curr_size + size + 1 > len -> join_len(rest, str, [curr | done], len)
end
end
def test do
nums = for _i <- 1..20, do: 1_234_567_890
join_len(nums, 100)
end
end
运行 test
产生:
[
"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
"1234567890 1234567890"
]
这是为了完整性。 Adam 发布并且我接受的答案更加简洁明了
def max_size_str(enumin, spacing) do
chunk_fun = fn element, acc ->
acc_sum = acc |> Enum.map(fn x -> List.last(x) end) |> Enum.sum()
if acc_sum + List.last(element) <= spacing do
if acc == [[0, 0]] do
{:cont, [element]}
else
{:cont, [element | acc]}
end
else
{:cont, acc, [element]}
end
end
after_fun = fn
element ->
{:cont, element, []}
end
enumin
|> Enum.map(fn x -> [x, String.length(Integer.to_string(x)) + 1] end)
|> Enum.chunk_while([[0, 0]], chunk_fun, after_fun)
|> Enum.map(&Enum.reverse(&1))
|> Enum.map(fn x -> x |> Enum.map(&List.first(&1)) end)
|> Enum.map(&Enum.join(&1, " "))
end
如果生成的字符串的总长度不是很大,并且您可以先连接所有数字(如您在原始问题中所述),那么涉及 Regex.scan/2
的解决方案可能会更加简洁。
nums = for _i <- 1..20, do: 1_234_567_890
Regex.scan(~r/\d.{1,99}(?=\s|\Z)/, Enum.join(nums, " "))
#⇒ [
# ["1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"],
# ["1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"],
# ["1234567890 1234567890"]
# ]
这里的正则表达式贪婪地寻找一个数字后跟最多 99 个符号,然后是 space 或输入结束(后者不包括在匹配中。)
出于好奇,这里有一个稍微修改过的 Adam 解决方案,它在函数子句中使用模式匹配来实现相同的结果。
defmodule Example do
def join_len(enum, len, acc \ [])
def join_len([], _len, acc), do: Enum.reverse(acc)
def join_len([num | rest], len, acc),
do: join_len(rest, len, join_num("#{num}", len, acc))
defp join_num(num, _, []), do: [num]
defp join_num(num, len, [head|tail])
when byte_size(num) + byte_size(head) < len,
do: [head <> " " <> num | tail]
defp join_num(num, _, acc), do: [num | acc]
def test do
nums = for _i <- 1..20, do: 1_234_567_890
join_len(nums, 100)
end
end
给定一个整数枚举(来自外部源),我需要将它们转换为字符串,然后加入它们(由 space 分隔)。
第二部分是将结果字符串拆分成多个chunk,每个chunk不超过100个字符,拆分不能在数字中间(所以拆分只能发生在插入数字之间的 space 上。
第一部分很简单。实际上将生成的字符串的大小保持在限制之下是一个挑战。
查看了 Enum。chunk_while/4 但不确定这是正确的方法。我还探索了在列表为整数时拆分列表并尝试加入结果但没有运气。
这是一个函数,join_len/2
,一次完成。它的工作原理是构建每个字符串,直到达到 100 个字符,然后移动到下一个字符串。
defmodule Example do
def join_len(enum, len), do: join_len(enum, "", [], len)
defp join_len([], curr, done, _len), do: Enum.reverse([curr | done])
defp join_len([num | rest], curr, done, len) do
str = Integer.to_string(num)
size = byte_size(str)
curr_size = byte_size(curr)
cond do
curr_size == 0 -> join_len(rest, str, done, len)
curr_size + size + 1 <= len -> join_len(rest, "#{curr} #{str}", done, len)
curr_size + size + 1 > len -> join_len(rest, str, [curr | done], len)
end
end
def test do
nums = for _i <- 1..20, do: 1_234_567_890
join_len(nums, 100)
end
end
运行 test
产生:
[
"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
"1234567890 1234567890"
]
这是为了完整性。 Adam 发布并且我接受的答案更加简洁明了
def max_size_str(enumin, spacing) do
chunk_fun = fn element, acc ->
acc_sum = acc |> Enum.map(fn x -> List.last(x) end) |> Enum.sum()
if acc_sum + List.last(element) <= spacing do
if acc == [[0, 0]] do
{:cont, [element]}
else
{:cont, [element | acc]}
end
else
{:cont, acc, [element]}
end
end
after_fun = fn
element ->
{:cont, element, []}
end
enumin
|> Enum.map(fn x -> [x, String.length(Integer.to_string(x)) + 1] end)
|> Enum.chunk_while([[0, 0]], chunk_fun, after_fun)
|> Enum.map(&Enum.reverse(&1))
|> Enum.map(fn x -> x |> Enum.map(&List.first(&1)) end)
|> Enum.map(&Enum.join(&1, " "))
end
如果生成的字符串的总长度不是很大,并且您可以先连接所有数字(如您在原始问题中所述),那么涉及 Regex.scan/2
的解决方案可能会更加简洁。
nums = for _i <- 1..20, do: 1_234_567_890
Regex.scan(~r/\d.{1,99}(?=\s|\Z)/, Enum.join(nums, " "))
#⇒ [
# ["1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"],
# ["1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"],
# ["1234567890 1234567890"]
# ]
这里的正则表达式贪婪地寻找一个数字后跟最多 99 个符号,然后是 space 或输入结束(后者不包括在匹配中。)
出于好奇,这里有一个稍微修改过的 Adam 解决方案,它在函数子句中使用模式匹配来实现相同的结果。
defmodule Example do
def join_len(enum, len, acc \ [])
def join_len([], _len, acc), do: Enum.reverse(acc)
def join_len([num | rest], len, acc),
do: join_len(rest, len, join_num("#{num}", len, acc))
defp join_num(num, _, []), do: [num]
defp join_num(num, len, [head|tail])
when byte_size(num) + byte_size(head) < len,
do: [head <> " " <> num | tail]
defp join_num(num, _, acc), do: [num | acc]
def test do
nums = for _i <- 1..20, do: 1_234_567_890
join_len(nums, 100)
end
end