让我们把这个问题分解成两个函数,一个用新的字符替换字符串中的元素,另一个用于字符串列表。
我会推荐类似的东西:
replaceCharInStr :: Int -> Char -> String -> String
replaceCharInStr 0 c (s:ss) = c:ss
replaceCharInStr n c (s:ss) = s : ???
replaceCharInStr n c [] = error "replaceCharInStr: Empty string"
这里我们说如果n 为0,则忽略c 字符串的第一个元素,然后如果n 不为0 并且列表至少有一个元素,则将该元素放在前面(练习留给读者。提示:递归),然后如果我们的字符串为空,则引发错误。我会说我不是特别喜欢这里使用error,最好返回Maybe String,或者我们可以说replaceCharInStr n c [] = [c]。我们还可以将类型签名更改为replaceCharInStr :: Int -> a -> [a] -> [a],因为这不是特定于字符串的。
对于下一个函数,我们要做的是获取一个索引,并在该索引处应用一个函数。一般来说,这个函数会有类型
applyAt :: Int -> (a -> a) -> [a] -> [a]
并且可以与replaceCharInStr 类似地实现
applyAt :: Int -> (a -> a) -> [a] -> [a]
applyAt 0 f (x:xs) = f x : xs
applyAt n c (x:xs) = x : ???
applyAt n c [] = error "applyAt: Empty list"
实际上,这与replaceCharInStr 的形状完全相同,所以如果你实现了这个,那么你应该能够按照applyAt 来实现replaceCharInStr
replaceCharInStr n c xs = applyAt n (\x -> c) xs
-- Or = applyAt n (const c) xs
那么你的replaceChar函数可以实现为
replaceChar :: Int -> Int -> Char -> [String] -> [String]
replaceChar n m c strings = applyAt n (replaceCharInStr m c) strings
-- Or = applyAt n (applyAt m (const c)) strings
剩下的就是实现applyAt。