julia 原生replace函数不能传入Dict?

看了下官方文档中关于replace函数的介绍
文中讲到替换前后的子字符串以1个或多个Pair类型pat=>r传入,即 replace(s::AbstractString, pat=>r, [pat2=>r2, …]; [count::Integer]) 。

replace("abcabc", "a" => "b", "b" => "c", r".+" => "a")
# output: "bca"

在实际使用过程中,我们一般事先有一系列替换前和替换后的字符串Pair。如果能用这一系列Pair来构造一个Tuple或Dict,再把Tuple或Dict传入replace,有利于反复使用这一系列Pair(其实是一个Tuple或Dict)。

但是,实操后,发现:replace函数不接受传入Tuple或Dict(当然,本人事先未利用多重派发机制来改造replace函数。)

大家在涉及字符串的批量替换操作时,是怎样使用简便replace函数的?有经验的朋友,欢迎回复。
谢谢!

let
	astring = "i am not any expert, neither an experienced one, but I'd like to cou re nao"
	uncode = "abcde"
	decode = "12345"
	book = Dict(i=>j for (i,j) in zip(uncode,decode))
	replace(astring,book...)	
end

"i 1m not 1ny 5xp5rt, n5ith5r 1n 5xp5ri5n354 on5, 2ut I'4 lik5 to 3ou r5 n1o"

另外!

 replace("abcsaasasas",r".+" => "a","a"=>"k") == replace("abcsaasasas","a"=>"k",r".+"=>"n") # false

replace("abcsaasasas","a"=>"k","c"=>"n") == replace("abcsaasasas","c"=>"n","a"=>"k") # true

我的传入Dict,结果是这样的:

dct = Dict([("a" , "k" ), ("b" , "d")]) ;
replace("abcabc", dct,  count=2)
MethodError: no method matching replace(::String, ::Dict{String, String})
Closest candidates are:
  replace(::Union{Function, Type}, ::Any; count) at /usr/share/julia/base/set.jl:622
  replace(::String, ::Pair...; count) where N at /usr/share/julia/base/strings/util.jl:535
  replace(::AbstractString, ::Pair...; count) at /usr/share/julia/base/strings/util.jl:634
  ...

Stacktrace:
 [1] top-level scope
   @ In[9]:2
 [2] eval
   @ ./boot.jl:373 [inlined]
 [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
   @ Base ./loading.jl:1196


把你的代码复制并跑了一下,是没问题的。
比照了咱们二人的字典,区别在于String还是Char。

你的

Dict{Char, Char} with 5 entries:
  'a' => '1'
  'c' => '3'
  'd' => '4'
  'e' => '5'
  'b' => '2'

我的

Dict{String, String} with 2 entries:
  "b" => "d"
  "a" => "k"

实际上,肯定是 Dict{String, String}比Dict{Char, Char} 更为常见。

implement replace on String for multiple patterns by vtjnash · Pull Request #40484 · JuliaLang/julia · GitHub

1.7开始支持的replace("abcabc", dct...)

感谢 @xgdgsc @AndyJado 的回复。
考虑到其他用户可能有相同需求,即把Dict传入replace函数,在此,我当个搬运工,综合两位提供的方法和链接,小结如下。(测试版本:Julia 1.7.2

想实现的效果

事先把替换前后的键值对定义到字典中,是方便的。大家应该不会否认吧?
举个例子,希望通过以下语句:

strs = "Li Lei uses Python.";
dct = Dict("Li Lei"=>"Han Meimei", "Python"=>"Julia");  
replace(strs, dct)

得到如下输出:

"Han Meimei uses Julia."

报错

实际运行时,报错了。

MethodError: no method matching replace(::String, ::Dict{String, String})
Closest candidates are:
  replace(::Union{Function, Type}, ::Any; count) at /usr/share/julia/base/set.jl:622
  replace(::String, ::Pair...; count) where N at /usr/share/julia/base/strings/util.jl:535
  replace(::AbstractString, ::Pair...; count) at /usr/share/julia/base/strings/util.jl:634
  ...

Stacktrace:
 [1] top-level scope
   @ In[16]:3
 [2] eval
   @ ./boot.jl:373 [inlined]
 [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
   @ Base ./loading.jl:1196


解决办法探讨

方法1:…召唤魔法(所知有限,随便这么叫一下,我一时不知怎么对其称呼)
@xgdgsc 道出了这个技能。

strs = "Li Lei uses Python.";
dct = Dict("Li Lei"=>"Han Meimei", "Python"=>"Julia");  
replace(strs, dct) # 错误
replace(strs, dct...) # 正确

方法2: 通过构造函数,来实现映射。这是 @xgdgsc 链接中提及的方法。

strs = "Li Lei uses Python.";
reg = r"Li Lei|Python";
dct = Dict("Li Lei"=>"Han Meimei", "Python"=>"Julia"); 
replace(strs, reg=>(x->dct[x]))  # 不熟悉的小伙伴,注意下:x->dct[x]构造了一个函数,它的输入值为x,输出值为dct[x]。

输出

"Han Meimei uses Julia."

从链接中的表述来看,这种方法的运算效率不错。

办法3: 更进一步地,利用多重派发机制,用户自行为replace函数增加能处理(::String, ::Dict{String, String})的新方法。比如,把方法1或者用户自行构思的其它方法增加到replace函数中。

原生replace函数接受一系列Pair或一个Dict{Char, Char}

Pair时: 来自官方的例子

replace([1, missing], missing=>0)

输出

2-element Vector{Int64}:
 1
 0

Dict{Char, Char}时: 来自 @AndyJado 的例子

let
	astring = "i am not any expert, neither an experienced one, but I'd like to cou re nao"
	uncode = "abcde"
	decode = "12345"
	book = Dict(i=>j for (i,j) in zip(uncode,decode))
	replace(astring,book...)	
end

输出

Dict{Char, Char} with 5 entries:
  'a' => '1'
  'c' => '3'
  'd' => '4'
  'e' => '5'
  'b' => '2'

省略号不是随便打的

mean while, julia内置的正则表达式只有r接字符串这一种方式么?

function regeX(a::String)
	strs="r\"$a\""
	Meta.parse(strs)|>eval
end
a = "oil fish"
regeX(a) == r"oil fish" # true

我印象中eachmatch是一个非常好用的函数,但是怎么用于替换我记不起了

感谢你提供的magic trick。真赞。
对于我这种编程小白来说,Julia世界真是个魔法村。
刚才专门又看了下自带的帮助文档(?replace),都没提及这种骚操作。

replace(A, old_new::Pair...; [count::Integer])

Return a copy of collection A where, for each pair old=>new in old_new, all occurrences of old are replaced by new. Equality is determined using isequal. If count is specified, then replace at most count occurrences in total.

The element type of the result is chosen using promotion (see promote_type) based on the element type of A and on the types of the new values in pairs. If count is omitted and the element type of A is a Union, the element type of the result will not include singleton types which are replaced with values of a different type: for example, Union{T,Missing} will become T if missing is replaced.

See also replace!, splice!, delete!, insert!.

!!! compat "Julia 1.7" Version 1.7 is required to replace elements of a Tuple.

我的知识盲点太多,接不住你的这些提问 :sweat_smile:
跟你们的讨论,使我很受益。很有启发。
谢谢!

常见问题 · Julia中文文档…-%E8%BF%90%E7%AE%97%E7%AC%A6%E7%9A%84%E4%B8%A4%E4%B8%AA%E7%94%A8%E6%B3%95%EF%BC%9Aslurping-%E5%92%8C-splatting

代码风格指南 · Julia中文文档

找见了你们二位提到的…符号
在这
此时,是该说“没事时候,多翻翻文档“,还是该说“有问题时候,到论坛里提问”? :grimacing:

补一句:交流,使人进步。

1 个赞

下晚,翻了下文档。eachmatch 在替换中,可以这么用:

场景1

#以下,把ST008替换为Test08

strs = "ST009ST008"
reg = r"(ST)(0+)([1-9])"
mch = collect(eachmatch(reg, strs))[2]
# replace(strs,mch=>"Test08")  # no
replace(strs,mch.match=>"Test08")  # yes

场景2
但是呢,如果想把第二个ST008替换为Test08呢?


strs = "ST008ST008"
reg = r"(ST)(0+)([1-9])"
mch = collect(eachmatch(reg, strs))[2]
replace(strs, mch.match=>"Test08")  

想要的结果

"ST008Test08"

实际输出,这不是我们想要的。

"Test08Test08"