0

我必须定义一个名为 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
4

6 回答 6

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!考虑以下语句:“对于集合 s 中的每个项目 x,x > 0”。如果 set s 为空,则该语句为真!这是真的,因为 s 中根本没有任何项目。在我看来,这就像一个类似的情况。

于 2012-04-24T00:26:39.303 回答
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)
于 2012-04-24T01:00:19.367 回答
1

您是否想过通过过滤每个列表以仅获得零然后比较列表的长度以查看它们是否相等来一次性完成此操作?

zeroCompare xs ys = cZeroes xs == cZeroes ys
  where
    cZeroes as = length $ filter (== 0) as
于 2012-04-23T23:51:05.133 回答
0

代替lengthand filter,您可以获取 predicate 的结果p,将其转换为 0 或 1,然后对结果求和:

count p = sum . map (fromEnum.p)

--or

import Data.List
count p = foldl' (\x -> (x+).fromEnum.p) 0

在你的情况下,p当然是(==0). 转换BoolInt使用fromEnum是一个非常有用的技巧。

于 2012-04-24T08:33:45.600 回答
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
于 2012-04-24T08:47:01.407 回答
0

我会将问题分解为涉及辅助函数的较小问题。

这就是我将其分解的方式:

  1. 比较两个计数的主要功能
  2. 计数辅助函数

首先:您需要一种方法来计算列表中零的数量。例如,如果在整数列表中搜索 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
于 2018-02-17T21:33:54.707 回答