Pau*_*-AG 3 haskell haskell-lens
如何编写一个基于镜头的函数,增加数字后缀(如果存在)并在没有这样的后缀时保持字符串不变?
"aaa1" -> "aaa2"
"aaa_123" -> "aaa_124"
"" -> ""
"aaa" -> "aaa"
"123a" -> "123a"
"3a5" -> "3a6"
Run Code Online (Sandbox Code Playgroud)
我觉得我可能首先需要一些棱镜,但不知道是哪一个(suffixed没有函数作为参数,而是标量)。
确定您需要哪种光学器件的一种快速方法是考虑它应具有的特性。对于您的情况,我们可以提出我的这个答案所建议的问题,并注意:
就像棱镜一样,您的光学器件可能会出现故障,因为可能没有数字后缀。
然而,与棱镜不同的是,您的光学器件是不可逆的。suffixed是可逆的,因为它的目标是前缀,而匹配的后缀是固定的。在您的情况下,目标是后缀,并且无法提前知道前缀。
缺乏可逆性表明您需要遍历,而不是棱镜。使用镜头库,实现遍历只需编写一个适合以下Traversal类型的函数:
type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t
Run Code Online (Sandbox Code Playgroud)
以下是实现您目的的一种方法:
import Control.Lens
import Text.Read (readMaybe)
import Data.Char (isDigit)
import Data.List.Extra (spanEnd)
-- numericSuffix :: Applicative f => (Integer -> f Integer) -> String -> f String
numericSuffix :: Traversal' String Integer
numericSuffix f str =
(prefix ++) <$> maybe (pure "") (fmap show . f) (readMaybe suffix)
where
(prefix, suffix) = spanEnd isDigit str
Run Code Online (Sandbox Code Playgroud)
实施注意事项:
此处进行的往返操作Read将从Show前缀中去除前导零。如果这是不可取的(这似乎很可能),那么您将需要比show格式化修改后的后缀更精致的东西。
不过,如果上述往返不是问题,则可以通过使用棱镜作为_Show遍历来使实现变得非常紧凑:
numericSuffix f str = (prefix ++) <$> _Show f suffix
Run Code Online (Sandbox Code Playgroud)
然后可以通过以下方式直接增加后缀over:
incrementSuffix :: String -> String
incrementSuffix = over numericSuffix succ
Run Code Online (Sandbox Code Playgroud)
ghci> incrementSuffix "aaa1"
"aaa2"
ghci> incrementSuffix "aaa_123"
"aaa_124"
ghci> incrementSuffix ""
""
ghci> incrementSuffix "aaa"
"aaa"
ghci> incrementSuffix "123a"
"123a"
ghci> incrementSuffix "3a5"
"3a6"
Run Code Online (Sandbox Code Playgroud)