带镜头的增量后缀

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没有函数作为参数,而是标量)。

dup*_*ode 6

确定您需要哪种光学器件的一种快速方法是考虑它应具有的特性。对于您的情况,我们可以提出我的这个答案所建议的问题,并注意:

  • 就像棱镜一样,您的光学器件可能会出现故障,因为可能没有数字后缀。

  • 然而,与棱镜不同的是,您的光学器件是不可逆的。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)

实施注意事项:

  • spanEnd来自额外

  • 此处进行的往返操作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)