如何编写基于Lens的函数递增字符串数字后缀(无则保持原串)
基于Lens实现字符串数字后缀递增函数
需求说明
需要实现一个函数,遵循以下规则处理字符串:
- 若字符串末尾带有连续数字后缀,将该数字值加1
- 无数字后缀、数字不在末尾时,保持原字符串不变
示例效果:
"aaa1" -> "aaa2" "aaa_123" -> "aaa_124" "" -> "" "aaa" -> "aaa" "123a" -> "123a" "3a5" -> "3a6"
解决方案
你提到的lens库中suffixed只能匹配固定后缀,无法处理动态的数字序列,因此需要自定义Prism/Traversal来实现需求。下面给出两种基于Lens风格的实现方式:
方式一:自定义Traversal实现
首先导入必要的模块:
import Control.Lens import Data.Text (Text) import qualified Data.Text as T import Data.Char (isDigit)
先实现字符串拆分逻辑,将字符串拆分为前缀和末尾的数字(如果存在):
splitEndDigits :: Text -> (Text, Maybe Integer) splitEndDigits t = case T.spanEnd isDigit t of ("", prefix) -> (prefix, Nothing) (digitStr, prefix) -> case reads (T.unpack digitStr) of [(n, "")] -> (prefix, Just n) _ -> (t, Nothing)
基于拆分逻辑定义Traversal,用于聚焦并修改末尾数字:
traverseEndingDigit :: Traversal' Text Integer traverseEndingDigit f t = case splitEndDigits t of (prefix, Just n) -> (\newN -> prefix <> T.pack (show newN)) <$> f n (prefix, Nothing) -> pure prefix
最后用over函数结合Traversal实现递增:
incrementEndingDigit :: Text -> Text incrementEndingDigit = over traverseEndingDigit (+1)
方式二:自定义Prism实现
如果更倾向于用Prism,可以定义一个匹配「前缀+末尾数字」的Prism:
_EndingDigitPair :: Prism' Text (Text, Integer) _EndingDigitPair = prism' embed match where embed (prefix, n) = prefix <> T.pack (show n) match t = case splitEndDigits t of (prefix, Just n) -> Just (prefix, n) _ -> Nothing
通过组合Prism和_2 Lens聚焦到数字部分,实现递增:
incrementEndingDigit :: Text -> Text incrementEndingDigit = over (_EndingDigitPair . _2) (+1)
测试验证
两种方式都能满足示例中的所有场景,比如:
incrementEndingDigit "aaa1" -- 返回 "aaa2" incrementEndingDigit "aaa_123" -- 返回 "aaa_124" incrementEndingDigit "" -- 返回 "" incrementEndingDigit "aaa" -- 返回 "aaa" incrementEndingDigit "123a" -- 返回 "123a" incrementEndingDigit "3a5" -- 返回 "3a6"
内容的提问来源于stack exchange,提问作者RandomB
相关产品推荐
相关产品推荐

