elixir - Elixir和字符串中的多个替换字符
问题描述
我是一个初学者,正在使用旧数据库,其中的字符像使用 Phoenix 框架的 Elixir 语言一样Ę,ę,Ń,ń
保存。;;;ca ...
我想在代码中多次替换该字符,我有一个功能:
def convert_content(content) do
content = String.replace(content, ";;;ca", "Ę")
content = String.replace(content, ";;;ea", "ę")
content = String.replace(content, ";;;d1", "Ń")
content = String.replace(content, ";;;f1", "ń")
end
但它很慢.. 我找到了https://github.com/elixir-lang/elixir/pull/4474但它不起作用。感谢帮助。
解决方案
问题
我认为您的问题源于您遍历字符串n
时间的事实,如果您有n
要替换的字符。
因此,为了使单个字符串更快,您只需要遍历一次字符串。我认为,这将使它更快。除了滚动您自己的算法外,我没有看到关于如何做到这一点的直接答案。
因此,为了验证我的想法,我编写了一个小脚本来进行基准测试,结果证明您建议的实现是最快的。
测量
首先是关于我如何测试性能的说明。我生成了一个随机字符串来测试每个算法。因此,每个算法都使用相同的输入进行测试,生成输入不计入结果。
然后我运行每个算法 100 次,用:timer.rc/1
. 我将所有结果相加并除以 100 得到平均执行时间。
鉴于您的问题缺乏细节,我也使用了我自己的字母表。您可以根据需要更换它。我只假设每个要替换字符串的前缀是“;;;;”。
这里是字母表。
def alphabet do
%{
";;;;a" => "a",
";;;;b" => "b",
";;;;c" => "c",
";;;;d" => "d",
";;;;e" => "e",
";;;;f" => "f",
";;;;g" => "g",
";;;;h" => "h",
";;;;i" => "i",
";;;;j" => "j",
";;;;k" => "k",
";;;;l" => "l",
";;;;m" => "m",
";;;;n" => "n",
";;;;o" => "o",
";;;;p" => "p",
";;;;q" => "q",
";;;;r" => "r",
";;;;s" => "s",
";;;;t" => "t",
";;;;u" => "u",
";;;;v" => "v",
";;;;w" => "w",
";;;;x" => "x",
";;;;y" => "y",
";;;;z" => "z"
}
end
它被实现为一个映射,它应该给我们 O(log n) 查找。
解决方案 1
首先,我从一个幼稚的版本开始;你展示的那个。
def naive(input) do
alphabet()
|> Map.keys()
|> Enum.reduce(input, fn key, str ->
String.replace(str, key, alphabet()[key])
end)
end
在这里,您只需遍历字母表的所有键并检查它们是否存在于字符串中,如果存在,则替换所有它们。
输入大小为 10000 且运行 100 次的此函数的平均执行时间为1.40691 ms。
解决方案 2
我采取的第二种方法是使用此处另一个答案的建议,即使用String.replace/4
而不是手动检查每个出现。
请注意,为简洁起见,我在这里剪掉了一大块字母表。
def better(input) do
String.replace(
input,
[
";;;;a",
";;;;b",
...
";;;;y",
";;;;z"
],
fn
";;;;a" -> "a"
";;;;b" -> "b"
...
";;;;y" -> "y"
";;;;z" -> "z"
end
)
end
输入大小为 10000 且运行 100 次的此函数的平均执行时间为1.3419400000000001 毫秒
解决方案 3
最终解决方案是我自己,我尝试推出自己的算法。
这里的想法是遍历字符串,一旦我们看到字符串以四个“;”开头 字符,我们可以根据他的第五个字符进行替换。
def alphabet2 do
%{
?a => ?a,
?b => ?b,
...
?y => ?y,
?z => ?z
}
end
def process(cl, acc) do
case cl do
[] ->
acc
[?;, ?;, ?;, ?;, c | r] ->
new_char = alphabet2()[c]
process(r, [new_char | acc])
[c | r] ->
process(r, [c | acc])
end
end
def even_better(input) do
cl = String.to_charlist(input)
process(cl, []) |> Enum.reverse() |> List.to_string()
end
输入大小为 10000 且运行 100 次的此函数的平均执行时间为1.21495 毫秒。
结论
您的解决方案足够快,足以满足您的需求。我唯一可以推荐的做法是并行处理一批字符串。您不能使单个字符串更快,但您可以更轻松地更快地处理一堆字符串。
基准
我使用的基准代码如下。
avg_ms =
1..runs
|> Enum.map(fn _ -> :timer.tc(fn -> even_better(str) end) end)
|> Enum.reduce(0, fn {time, _}, acc -> acc + time end)
|> (fn s -> s / runs / 1000 end).()
IO.puts("Even Better took avg #{avg_ms} ms")
另请注意,使用一些宏可以使这些解决方案更漂亮。请参阅其他答案。
推荐阅读
- reactjs - setState 函数(功能组件)不会在控制台中更新状态
- karate - 空手道:验证 JSON 响应中的特定日期
- python-3.x - 为什么这个返回语句有效?(Python 3)
- javascript - 如何从 JavaScript 中的变量中删除 pin?
- mysql - NOT IN 和 != ALL 之间的区别
- c - 填充动态二维数组时出现段错误
- python - Python Selenium - ResourceWarning:启用 tracemalloc 以获取对象分配回溯
- r - 使用 mutate_at 在 R 中计算多列
- ssl - 如何更新 kubernetes ssl 证书
- facebook-graph-api - 有没有办法从graph api获取instagram商业帐户的类别?