我必须定义一个名为zeros的函数,它接受两个列表的输入并返回一个布尔值,如果数字0在每个列表中出现的次数相同,则返回True,否则返回false。
这是我作业中的最后一个问题,我已经设法解决了问题,但是我想知道是否有人可以找到减少代码量的方法,任何想法都会受到赞赏。到目前为止,我的代码如下:
x :: Int
x = 0
instances::[Int]->Int
instances [] = 0
instances (y:ys)
| x==y = 1+(instances ys)
| otherwise = instances ys
zeros :: [Int] -> [Int] -> Bool
zeros [] [] = False
zeros x y
| ((instances x) == (instances y)) = True
| otherwise = False
答案 0 :(得分:3)
如果不做太多,因为这是作业,这里有一些提示。
你知道列表推导吗?在这种情况下,它们会很有用。例如,您可以将它们与if
表达式结合起来执行以下操作:
*Main> let starS s = [if c == 's' then '*' else ' ' | c <- s]
*Main> starS "schooners"
"* *"
您甚至可以使用它们进行过滤。例如:
*Main> let findFives xs = [x | x <- xs, x == 5]
*Main> findFives [3,7,5,6,3,4,5,7,5,5]
[5,5,5,5]
这些都不是一个完整的答案,但不应该很难看到如何使这些结构适应你的情况。
您还应该考虑一下您是否真的需要一名警卫!例如,这是一个用与你的风格相同的守卫写的函数:
lensMatch [] [] = True
lensMatch xs ys
| ((length xs) == (length ys)) = True
| otherwise = False
这是一个做同样事情的功能!
lensMatch' xs ys = length xs == length ys
你可以看到它们是一样的;测试第一个:
*Main> lensMatch [1..4] [1..4]
True
*Main> lensMatch [1..4] [1..5]
False
*Main> lensMatch [] [1..5]
False
*Main> lensMatch [] []
True
测试第二个:
*Main> lensMatch' [1..4] [1..4]
True
*Main> lensMatch' [1..4] [1..5]
False
*Main> lensMatch' [] [1..5]
False
*Main> lensMatch' [] []
True
最后,我非常同意sblom上面的评论; zeros [] []
应为True
!考虑以下陈述:“对于集合中的每个项目x,x> 0”。如果set s为空,则该语句为真!这是真的,因为根本没有物品。在我看来,这似乎是一种类似的情况。
答案 1 :(得分:2)
我无法相信没有人建议使用foldr
。不是最短或最好的定义,但IMO是最有教育意义的:
instances :: Eq a => a -> [a] -> Int
instances n = foldr incrementIfEqual 0
where incrementIfEqual x subtotal
| x == n = subtotal + 1
| otherwise = subtotal
zeros :: Num a => [a] -> [a] -> Bool
zeros xs ys = instances 0 xs == instances 0 ys
虽然对instances
的一个非常简短的定义,我想出的基本上与Abizern相同:
instances :: Eq a => a -> [a] -> Int
instances x = length . filter (==x)
答案 2 :(得分:1)
您是否曾想过在一次过程中通过过滤每个列表以获得零而然后比较列表的长度来查看它们是否相等?
zeroCompare xs ys = cZeroes xs == cZeroes ys
where
cZeroes as = length $ filter (== 0) as
答案 3 :(得分:0)
而不是length
和filter
,您可以获取谓词p
的结果,将其转换为0或1,并对结果求和:
count p = sum . map (fromEnum.p)
--or
import Data.List
count p = foldl' (\x -> (x+).fromEnum.p) 0
在您的情况下,p
当然是(==0)
。使用Bool
将Int
转换为fromEnum
是一个非常有用的技巧。
答案 4 :(得分:0)
另一个想法是同时处理这两个列表,这有点冗长,但很容易理解:
zeros xs ys = cmp xs ys == 0 where
cmp (0:xs) ys = cmp xs ys + 1
cmp xs (0:ys) = cmp xs ys - 1
cmp (_:xs) ys = cmp xs ys
cmp xs (_:ys) = cmp xs ys
cmp [] [] = 0
答案 5 :(得分:0)
我会将问题分解为涉及辅助函数的较小问题。
这就是我打破它的方式:
首先:您需要一种方法来计算列表中的零数量。例如,如果在整数列表中搜索0的数量,我会通过执行以下操作来实现此目的:
count :: [Int] -> Int
count xs = foldl (\count num -> if num == 0 then (count + 1) else count) 0 xs
第二:你需要一种方法来比较两个列表的数量。本质上,您需要一个函数,它将两个列表作为参数,计算每个列表的计数,然后根据结果返回一个布尔值。例如,如果每个列表都是一个int列表,则与上面的计数示例相对应:
equalZeroes :: [Int] -> [Int] -> Bool
equalZeroes x y = (count x) == (count y)
您还可以在equalZeroes函数中的where关键字下定义count,如下所示:
equalZeroes :: [Int] -> [Int] -> Bool
equalZeroes x y = (count x) == (count y)
where
count :: [Int] -> Int
count xs = foldl (\count num -> if num == 0 then (count + 1) else count) 0 xs
运行此代码时,调用该函数将返回所需的布尔值:
equalZeroes [0,1,4,5,6] [1,4,5,0,0]
-> False
equalZeroes [0,1,4,5,6] [1,4,5,0]
-> True