如何用purrr::reduce2实现嵌套gsub调用的字符串替换效果
用purrr实现批量字符串替换(替代嵌套gsub)
咱们先明确需求:有这么一个字符串向量:
tst_str <- c("abc", "123", "klm", "lop")
需要完成这几组替换:
abc替换成za12123替换成poiklm替换成uyt
最直接的写法是嵌套调用gsub,确实能得到预期结果:
gsub( pattern = "abc", replacement = "za12", x = gsub( pattern = "123", replacement = "poi", x = gsub( pattern = "klm", replacement = "uyt", x = tst_str ) ) ) # 输出:[1] "za12" "poi" "uyt" "lop"
但嵌套写法不够优雅,想用purrr::reduce2简化时却碰到了警告:
purrr::reduce2( .x = c("abc", "123", "klm"), .y = c("za12", "poi", "uyt"), .f = function(x, y, init) { gsub(pattern = x, replacement = y, x = init) }, .init = tst_str )
Warning message: In gsub(pattern = x, replacement = y, x = init) :
argument 'pattern' has length > 1 and only the first element will be used
问题出在哪?
你写的reduce2回调函数参数顺序搞反啦!reduce2要求回调函数的参数顺序是:累积值(也就是.init的初始值,或者上一步的计算结果)、.x的当前元素、.y的当前元素。但你把参数写成了x, y, init,这就导致变量逻辑完全错位,进而触发了长度警告。
正确的解决方案
方案1:修正reduce2的参数顺序
把回调函数的参数调整为acc, pat, rep(acc代表累积值,pat是当前要匹配的模式,rep是对应的替换值),逻辑就通顺了:
library(purrr) tst_str <- c("abc", "123", "klm", "lop") patterns <- c("abc", "123", "klm") replacements <- c("za12", "poi", "uyt") reduce2( .x = patterns, .y = replacements, .f = function(acc, pat, rep) { gsub(pattern = pat, replacement = rep, x = acc) }, .init = tst_str ) # 输出:[1] "za12" "poi" "uyt" "lop"
这样不仅没有警告,结果和嵌套gsub完全一致。
方案2:先生成替换函数,再用reduce组合
另一种更具函数式风格的写法:先把每一组替换规则转换成独立的函数,再用reduce依次把这些函数作用到初始向量上:
# 生成每个替换对应的函数 replace_funs <- map2(patterns, replacements, function(pat, rep) { function(vec) gsub(pat, rep, vec) }) # 依次执行这些函数 reduce(replace_funs, ~ .y(.x), .init = tst_str) # 同样得到正确结果
这里的~ .y(.x)是purrr里的简写,意思是把上一步的结果(.x)传给当前函数(.y)。
方案3:用基础R的Reduce实现(无需purrr)
如果不想依赖purrr,基础R的Reduce配合Map也能搞定:
replace_funs <- Map(function(pat, rep) function(vec) gsub(pat, rep, vec), patterns, replacements) Reduce(function(acc, fun) fun(acc), replace_funs, init = tst_str)
效果和前面的方案完全相同。
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

