尝试学习Haskell。我正在尝试编写一个简单的函数来从列表中删除一个数字而不使用内置函数(删除...我认为)。为简单起见,我们假设输入参数是一个Integer,列表是一个Integer列表。这是我的代码,请告诉我以下代码有什么问题
areTheySame :: Int -> Int-> [Int]
areTheySame x y | x == y = []
| otherwise = [y]
removeItem :: Int -> [Int] -> [Int]
removeItem x (y:ys) = areTheySame x y : removeItem x ys
答案 0 :(得分:28)
其他人是正确的,问题是:
运算符。我会说,无论如何,返回列表的areTheySame
函数是错误的方法。而不是切换到++
运算符,该函数的更好实现将是:
removeItem _ [] = []
removeItem x (y:ys) | x == y = removeItem x ys
| otherwise = y : removeItem x ys
如您所见,这是一个非常简单的实现。此外,像这样的消费对你的程序来说要比将一堆列表附加在一起要少得多。它还有其他好处,比如懒惰地工作。
答案 1 :(得分:10)
:
运算符没有按照您的想法执行:
(:) :: a -> [a] -> [a]
它需要a
类型的项目,并将其添加到类型a
列表的开头。您正在使用它来加入两个类型为a
的列表。为此,您需要使用++
:
(++) :: [a] -> [a] -> [a]
另外,如果你创建一个递归函数,它需要一个结束条件。所以试试这个:
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
这样,当你到达列表的末尾时,该函数将停止递归。
答案 2 :(得分:9)
您也可以将此作为列表理解
delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]
答案 3 :(得分:3)
这是使您的示例有效的最小修复:
removeItem :: Int -> [Int] -> [Int]
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
首先,您需要使用++
来连接列表,因为您使用的:
运算符只将一个元素添加到列表的开头(它不能用于添加列表的一个元素)元素也不添加空列表)。首先将列表的头部(y
)与要删除的项目进行比较,并使用areTheySame
正确返回该项目或空列表。然后,您希望以递归方式继续在列表的其余部分removeItem
上使用ys
。结果列表需要使用++
连接。
其次,正如Chris Lutz所说,当你到达列表的末尾时,你需要一个结束条件。通过添加这一行,Haskell知道如何处理空列表(即没有,只返回一个空列表)。
正如Chuck所说,你可以通过让removeItem不委托比较的任务来简化这个任务的代码,但是比较它自己并丢弃它应该被移除的元素,否则将它保留在列表头部(使用{ {1}})。在任何情况下,继续递归列表的其余部分。
:
答案 4 :(得分:3)
供参考,您可能有兴趣看到how it's done in delete
from Data.List。
您可以保留areTheySame
,但是您需要在removeItem
中使用concatMap
来折叠空列表:
removeItem :: Int -> [Int] -> [Int]
removeItem x xs = concatMap (areTheySame x) xs
或等效
removeItem :: Int -> [Int] -> [Int]
removeItem x = concatMap (areTheySame x)
请注意,您的功能类型可能更为通用:
areTheySame :: (Eq a) => a -> a -> [a]
removeItem :: (Eq a) => a -> [a] -> [a]
这允许从已定义==
的任何类型的列表中删除项目,而不仅仅是Int
。
答案 5 :(得分:3)
我只用一行代码写了一个函数:
remove element list = filter (\e -> e/=element) list
例如:
remove 5 [1..10]
[1,2,3,4,6,7,8,9,10]
remove 'b' ['a'..'f']
“acdef”
答案 6 :(得分:0)
我相信到目前为止给出的所有解决方案与Data.List.delete的工作方式不同,Data.List.delete只删除第一个成员。
deleteFromList x xs =
case break (==x) xs of
(_,[]) -> xs
(notsat,sat) -> notsat ++ tail sat
我试图仅删除第一个成员(尚未在D.L处达到峰值)。
目前还不清楚顶级海报想要采取哪种行为。