返回列表的最大值和出现次数的函数

时间:2016-11-04 21:08:41

标签: haskell recursion functional-programming higher-order-functions

我正在写一个递归函数mxAndC。当我给它一个列表时,它应该返回一个元组。元组将给定列表的最大值作为其第一个元素,第二个元素将是元素在列表中出现的次数。赋值不允许我创建辅助函数。我期待以下输出:

mxAdC "bananas" = (s,1)

mxAdC "banana" =(n,2)

mxAdC [mod x 4 | x <- [1..50]] -> (3,12)

我做了以下事情:

mxAdC = go 0
 where go count (x:xs)  
           | count /= 0      = (mx, count)
           | null ((x:xs))   = error "The list is empty"
           | x == mx         = go (count+1) xs
         where mx = maximum (x:xs)

我收到错误:

* Ambiguous type variable `a0' arising from a use of `go'
      prevents the constraint `(Ord a0)' from being solved.
      Relevant bindings include
        mxAdC :: [a0] -> (a0, Integer) (bound at hw06.hs:24:1)
      Probable fix: use a type annotation to specify what `a0' should be.
      These potential instances exist:
        instance (Ord a, Ord b) => Ord (Either a b)
          -- Defined in `Data.Either'
        instance Ord Ordering -- Defined in `GHC.Classes'
        instance Ord Integer
          -- Defined in `integer-gmp-1.0.0.1:GHC.Integer.Type'
        ...plus 23 others
        ...plus 38 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    * In the expression: go 0
      In an equation for `mxAdC':
          mxAdC
            = go 0
            where
                go count (x : xs)
                  | count /= 0 = (mx, count)
                  | null ((x : xs)) = error "The list is empty"
                  | x == mx = go (count + 1) xs
                  where
                      mx = maximum (x : xs)

我是Haskell的新手。在那里,任何仁慈的专家是否愿意伸出手来帮助这个新手?

3 个答案:

答案 0 :(得分:0)

你遇到了可怕的单态限制。

您可以通过

修复类型错误
  • 使用mxAdC x = go 0 x
  • {-# LANGUAGE NoMonomorphismRestriction #-}放在文件的开头
  • 添加类型签名

你的功能仍然是错误的,但现在却出现了问题。

Haskell禁止看起来像顶级常量的多态函数(在=符号之前没有参数)。所以通常你不能eta-reduce(用foo x = bar x替换foo = bar)。完整的解释相当长 - 请参阅此答案:https://stackoverflow.com/a/32496865/805266

答案 1 :(得分:0)

相当简单的实现将使用filter

mxAdC :: Ord a => [a] -> (a,Int)
mxAdC xs = (mx,length $ filter (\x -> x == mx) xs) where mx = maximum xs

答案 2 :(得分:0)

其他人已经解释了类型错误。让我们来看看更有趣的错误。

  | null ((x:xs))   = error "The list is empty"

怎么了? x : xs永远不会为空。它不可能是,因为它的第一个元素是x!你打算做什么是

mxAdC ys
  | null ys = error "mxAdC: the list is empty"
  | otherwise = go 0 ys
  where ...

下一个问题是

  | count /= 0      = (mx, count)

这说明一旦计数非零,你就完成了。所以你永远不会超过1.你认识到你的递归需要一个基本情况,所以你试图把它放入,但你错过了标记。您需要的基本情况是处理空列表:

go count [] = ?
go count (x:xs)  
  | x == mx         = go (count+1) xs

go仍然遗漏了某些内容。在x /= mx时你想要发生什么?你需要再添加一个案例(我留下了问号供你填写):

go count [] = ?
go count (x:xs)  
  | x == mx         = go (count+1) xs
  | otherwise       = ?

最后一个主要问题是您在mx函数中定义了go。这将计算每次递送列表go的任何部分的最大值。您要做的是在mx

中定义mxAdC
mxAdc ys
  | ....
  where
    go ...
    mx = maximum ys

还有两个很大的效率问题需要处理。一个问题是go不再强制在递归调用中计算累加器,这可能导致空间泄漏。使用{/ 1}}语言扩展名

可以轻松修复此问题
BangPatterns

最后一个问题是您仍然遍历列表两次:一次计算最大值,然后再次计算它发生的次数。只需一次就可以完成。我不会给你所有的细节,但基本的诀窍是改变你的go !count [] = ? go !count (x:xs) | x == mx = go (count+1) xs | otherwise = ? 函数,以获取迄今为止看到的最大元素以及它被看到的次数。你必须调整go的其余部分才能适应,使用模式匹配来取代mxAdC