我是Haskell的新手。我正在尝试编写一个函数,给定列表中存在的 l 列表元素 x ,以及要插入 y 的元素:在列表l中第一次出现元素x之前插入元素y。如果元素x在列表中存在不,则保持列表不变。
我在这方面遇到很多麻烦,并希望得到任何建议。
这是我试过的('n'是第一次出现的元素x):
insertSpecial :: Eq a => a -> a -> [a] -> [a]
insertSpecial let (ys,zs) = splitAt n xs in ys ++ [y] ++ zs
答案 0 :(得分:6)
怎么样
insertSpecial :: Eq a => a -> a -> [a] -> [a]
insertSpecial x y [] = []
insertSpecial x y (a:as) | a == x = y:a:as
insertSpecial x y (a:as) = a : insertSpecial x y as
此方法使用称为递归的一般方法。这意味着手头的任务被分解为一个或几个基本情况,为此可以轻松定义结果,以及可以通过将工作分解为部分。在一般情况下,该函数随后在较小的任务上被称为递归。目标是最终以一个基本案例结束工作完成的基础案例。另请参阅this part from Learn you a Haskell。
对于insertSpecial
,我们可以定义两个基本情况:
如果列表为空,则它不包含我们正在寻找的元素,我们希望在这种情况下保持列表不变,所以我们只返回一个空列表。我们已经完成了。
如果列表不为空和,则第一个元素是我们要查找的元素,我们将y
放在此列表的前面并返回该元素。我们又完成了。
这让我们看到列表不为空但第一个元素不是我们正在寻找的那个元素的情况。在这种情况下,我们将工作(列表)分为两部分:第一个元素和其余元素。我们将第一个元素放在列表的前面,通过在列表的其余部分调用insertSpecial
来返回。这就是递归发生的地方:insertSpecial
调用自己。
关于这一点可能有点难以理解的一件事是,最后一种情况如何通过在正确的位置插入元素来生成仅与原始列表不同的列表。让我们考虑一个例子。假设我们有列表
['h','e','l','o']
请注意,在Haskell中,常规字符串只是字符列表['h','e','l','o'] == "helo"
。另请注意,['h','e','l','o']
确实是'h':'e':'l':'o':[]
的语法糖。 (:
接受一个元素和一个列表,并将该元素添加到列表中。它也称为 cons )。
现在让我们在'l'
:
'o'
insertSpecial 'o' 'l' "helo"
由于"helo"
不为空,'h'
绑定到a
而"elo"
绑定到as
(此外,'o'
必须绑定x
和'l'
绑定到'y'
)。由于a == 'h' /= 'o' == x
我们处于第三种情况,所以
insertSpecial 'o' 'l' ('h':"elo") = 'h' : insertSpecial 'o' 'l' "elo"
现在insertSpecial 'o' 'l' "elo"
我们再次陷入第三种情况("elo"
不为空且'o' /= 'e'
):
insertSpecial 'o' 'l' ('e':"lo") = 'e' : insertSpecial 'o' 'l' "lo"
这又引发了第三种情况:
insertSpecial 'o' 'l' ('l':"o") = 'l' : insertSpecial 'o' 'l' "o"
现在在最后一次调用中,我们实际上将'o'
绑定到a
,等于x
,我们在第二种情况下着陆,我们在前面加{{1}的值(y
)到'l'
(a
)和'o'
(as
),所以我们得到:
[]
将所有这些替换放在一起我们得到:
insertSpecial 'o' 'l' ('o':[]) | 'o' == 'o' = 'l' : 'o' : []
如上所述insertSpecial 'o' 'l' "helo" =
'h' : insertSpecial 'o' 'l' "elo" =
'h' : 'e' : insertSpecial 'o' 'l' "lo" =
'h' : 'e' : 'l' : insertSpecial 'o' 'l' "o" =
'h' : 'e' : 'l' : 'l' : 'o' : []
。耶。
答案 1 :(得分:4)
您可以在满足标准的第一个位置拆分
,而不是在索引处拆分insertSpecial y x xs = front ++ [y] ++ back
where
(front, back) = break (== x) xs
如果您希望错误,如果您认为x
是列表元素的假设不满意,
insertSpecial y x xs = case break (== x) xs of
(front, back@(_:_)) -> front ++ [y] ++ back
_ -> error "Didn't find element"
但直接递归也是一个非常好的解决方案。
答案 2 :(得分:1)
有三种情况
insertSpecial :: Eq a => a -> a -> [a] -> [a]
insertSpecial _ _ [] = ?
insertSpecial x y (a:as) | a == x = ?
| otherwise = a : rest
where rest = ?
每个都会发生什么?
第一种情况是您确切知道元素不在列表中,因为列表为空。然后返回相同的空列表。
下一种情况是列表的第一个元素是您要查找的元素。然后你只需要返回相同的列表,前面加上y。
最后一个递归的情况(otherwise
)是第一个元素不是你要查找的元素,但它可能仍然在列表的其余部分。然后,您需要将a
添加到列表的其余部分,其中列表的其余部分为as
,y
在第一次出现x
之前加上{{1}}。
答案 3 :(得分:1)
insertSpecial::[Int]->Int->Int->[Int]
insertSpecial [] _ _ = []
insertSpecial list a b = concat [if x == a then [b] ++ [x] else [x] | x<-list]
在
之前插入b答案 4 :(得分:1)
以fold
表示的一般谓词beforeEvery
beforeEvery pred y = foldr (\ x xs -> if pred x then y : x : xs else x : xs) []
在第一次出现后明确展开的递归函数终止
before :: (a -> Bool) -> a -> [a] -> [a]
before _ _ [] = []
before pred y (x : xs) | pred x = y : x : before pred y xs
| otherwise = x : before pred y xs
这样
beforeElem x y = before (== x) y
或
beforeElem :: Eq a => a -> a -> [a] -> [a]
beforeElem x y = before (== x) y
含义
beforeElem 3 89 [1,2,3,4,5,3,4,5] == [1,2,89,3,4,5,3,4,5]