我是Haskell的新手和一般的函数式编程。我正在尝试实现一个函数来获取这样的列表
["abc", "def", "ghi"]
并且希望能够替换第y个元素中的第x个字符,例如
replaceChar 1 2 'd' arr
会产生
["abc", "ded", "ghi"]
所以基本上第一个参数是元素,第二个是字符串的位置,第三个是字符,最后一个是[String]。
此功能的签名如下所示:
replaceChar :: Int -> Int -> Char -> [String] -> [String]
任何帮助将不胜感激。谢谢!
答案 0 :(得分:5)
首先要注意的是:虽然您的签名完全没问题,但您实际上并没有使用您处理字符串的事实,它也可以是任何其他类型的列表。通过使用完全通用的类型变量(小写字母)而不是Char
来表示您的签名中通常是一个好主意 1 :< / p>
replaceAtXY :: Int -> Int -> a -> [[a]] -> [[a]]
接下来,请注意,基本上可以将问题简化为修改普通(非嵌套)列表的 n -th元素。在外部列表中,您修改 y -th子列表,即在该子列表中修改 x -th元素。
那么&#34;修改&#34;在哈斯克尔意味着什么?我们不能突变当然 2 的元素。我们需要一个获取列表并返回另一个列表的函数,并根据对列表中单个元素进行操作的函数执行此操作。
modifyNth :: Int -> (a->a) -> [a]->[a]
注意这与标准函数map :: (a->b) -> [a]->[b]
有些相似。
一旦拥有该功能,您就可以轻松实现
modifyXY :: Int -> Int -> (a->a) -> [[a]]->[[a]]
modifyXY x y f nList = modifyNth y (modifyNth x f) nList
(顺便说一下,nList
参数不需要写,你可以η-reduce。)
1 至于为什么这是一个好主意:显然,它允许你在更常规的设置中使用该功能。但更重要的是,它为类型检查器提供了额外的信息,您无法对所包含的元素本身做任何事情。这实际上有助于在更复杂的应用程序中捕获大量错误!
2 实际上你可以在ST monad中使用相当不错的语义。
答案 1 :(得分:4)
让我们将这个问题分解为两个函数,一个用字符串替换字符串中的元素,另一个用字符串列表来替换它。
我会推荐类似的东西:
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
完全相同,所以如果你实现了这个,那么你应该能够replaceCharInStr
实现applyAt
1} p>
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
。
答案 2 :(得分:3)
如果你有爱德华·凯梅特的镜头套装,那么你的例子就是单行:
import Control.Lens
["abc", "def", "ghi"] & ix 1 . ix 2 .~ 'd'
返回
["abc","ded","ghi"]
镜头可以模拟您希望使用命令式语言的索引和属性访问,但是在Haskell中。如果您刚刚开始学习Haskell,那么在使用Lens之前您应该稍等一下。它既聪明又有力,但也很大而且复杂。
答案 3 :(得分:0)
试试这个:
replace n 0 c (x:xs) = (replace' n c x) : xs
replace n m c (x:xs) = x : (replace n (m-1) c xs)
where
replace' 0 c (x:xs) = c : xs
replace' n c (x:xs) = x : (replace' (n-1) c xs)
在这里,您只需遍历列表,直到相应的索引为0
,然后我们替换匹配列表中的字符。我们使用相同的原则来替换列表中的charachter。我们遍历它,当我们到达指定的索引时,我们用新的索引替换该索引处的字符。
最后,所有内容都相互纠缠,以取代旧结构,这次更换了角色。