Haskell-计算多个元素的出现次数并返回元组

Haskell-计算多个元素的出现次数并返回元组,haskell,recursion,Haskell,Recursion,嘿,我是Haskell初学者,我假装做以下功能: 事件37[-1,3,-4,3,4,3,-8,7,7,3] 我想要的输出: (4,2) 我做了这个尝试,但效果不太好,我想我在单独计算元素和返回元组时遇到了麻烦 occurrences a b [] = 0 occurrences a b (x:xs) | x == a = 1 + occurrences a b xs | x == b = 1

嘿,我是Haskell初学者,我假装做以下功能:

事件37[-1,3,-4,3,4,3,-8,7,7,3]

我想要的输出: (4,2)

我做了这个尝试,但效果不太好,我想我在单独计算元素和返回元组时遇到了麻烦

occurrences  a b [] = 0

occurrences  a b (x:xs) 
                       | x == a =  1 + occurrences  a b xs
                       | x == b =  1 + occurrences  a b xs
                       | otherwise = occurrences  a b xs

谢谢你给我的建议和帮助;)

你可以用很多不同的方法来解决这个问题,这里是一个折叠的例子

occurrences :: (Eq a) => a -> a -> [a] -> (Int, Int)
occurrences a b list = foldr (\y (a', b') -> ((isEqual y a a'), (isEqual y b b'))) (0, 0) list
  where isEqual listEle tupEle count = if (listEle == tupEle) then (count + 1) else count
问题之一是类型不匹配。您需要一种类型的:

(Int, Int)
但是,一旦您有一个空列表,您将在此处返回一种int类型:

occurrences  a b [] = 0 -- Int 
occurrences  a b (x:xs) 
                       | x == a =  1 + occurrences  a b xs 
                       | x == b =  1 + occurrences  a b xs
                       | otherwise = occurrences  a b xs
您需要某种类型的累加器,您可以通过在其中绑定一个本地函数来实现这一点,该函数接受您的起始元组
(0,0)
,或者您可以将其传递给如下事件:

occurrences :: Int -> Int -> [Int] -> (Int, Int) -> (Int, Int)
我建议使用局部函数,因为在这种方法中,您总是希望从(0,0)开始

occurrences' :: (Eq a) => a -> a -> [a] -> (Int, Int)
occurrences' a b list = go list (0,0)
  where go x (e1, e2) = if (x == []) then (e1, e2) else (go (tail x) ((isEqual a (head x) e1), (isEqual b (head x) e2)))
        isEqual v v' accum = if (v == v') then (accum + 1) else (accum)

这不是最惯用的方法,但它表明了这一点。您应该尝试使用类型来帮助解决此问题。

@emg184提供了解决此问题的好方法,但是可以有更干净、更容易阅读的方法来解决此问题。例如:

Prelude Control.Arrow> first (1+) (1,4)
(2,4)
Prelude Control.Arrow> second (1+) (1,4)
(1,5)
出现次数x y xs=(计数x xs,计数y xs) 其中count=(长度)。过滤器。(==)
count
也可以用更可读的格式编写:

count x=(length.filter(=x))

一个好方法是添加类型签名,并使用错误消息指导您:

occurrences :: (Eq a) => a -> a -> [a] -> (Int, Int)
occurrences a b [] = 0
occurrences a b (x:xs)
  | x == a = 1 + occurrences a b xs
  | x == b = 1 + occurrences a b xs
  | otherwise = occurrences a b xs
第一个错误是“无法从上下文
Eq a
中的文本
0
推断
(Num(Int,Int))
”。这意味着我们不能在第一个等式中使用
0
,因为它不是一个元组,或者更准确地说,没有
Num
实例允许我们通过
fromIntegral
将文本
0
转换为元组。在基本情况下,我们应该为两个和返回一个包含0的元组:

现在,这将产生预期的结果:

> occurrences 'a' 'b' "ababcb"
(2,3)
但我们可以从几个方面改进这个解决方案。首先,
a
b
在整个计算过程中保持不变,因此我们可以在助手函数中进行递归,而不是将
a
b
传递给每个调用

occurrences :: (Eq a) => a -> a -> [a] -> (Int, Int)
occurrences a b = go
  where
    go [] = (0, 0)
    go (x:xs)
      | x == a = let (m, n) = go xs in (m + 1, n)
      | x == b = let (m, n) = go xs in (m, n + 1)
      | otherwise = go xs
最后,我们可以将每个元素映射到一个值(0或1)并通过求和减少它们,而不是保留一个累加器并逐个添加元素。此映射/缩减模式由
foldMap::(可折叠t,monoidm)=>(a->m)->ta->m
捕获,它将容器(
ta
)的每个元素映射到一个值(
m
),并使用
Monoid
实例组合结果。这里使用的幺半群是来自
数据的
Sum
。幺半群
,其
monoid
半群
实例分别定义
mempty
=
Sum 0
Sum a Sum b
=
Sum(a+b)

import Data.Coerce (coerce)
import Data.Foldable (foldMap)
import Data.Monoid (Sum(..))

occurrences :: (Eq a) => a -> a -> [a] -> (Int, Int)
occurrences a b = coerce . foldMap go
  where
    go x
      | x == a = (Sum (1 :: Int), mempty)
      | x == b = (mempty, Sum (1 :: Int))
      | otherwise = mempty

我们可以使用像和这样的函数来构造一个元组,其中我们将一个函数应用于2元组的两个项之一。例如:

Prelude Control.Arrow> first (1+) (1,4)
(2,4)
Prelude Control.Arrow> second (1+) (1,4)
(1,5)
因此,我们可以使用以下内容更新元组:

import Control.Arrow(first, second)
import Data.List(foldl')

occurrences :: (Eq a, Integral i, Integral j, Foldable f) => a -> a -> f a -> (i, j)
occurrences a b = foldl' (flip f) (0, 0)
    where f c | a == c = first (1+)
              | b == c = second (1+)
              | otherwise = id
对于示例输入,这将生成:

Prelude Control.Arrow Data.List> occurrences 3 7 [-1,3,-4,3,4,3,-8,7,7,3]
(4,2)

通过使用镜头更新元组中的一个元素,我们可以很容易地扩展这种行为。

您在概念上非常接近-您似乎将元组与其中包含的数值混淆了。作为提示,请尝试在bxs上进行模式匹配。您的基本大小写也是错误的,因为您返回的是数字而不是元组。这很容易解决。是的,我知道我可以用
a b[]=0
来解决基本情况,但是我找不到一种方法来解决模式匹配问题,使之只和一个元组成员…函数将计算有多少
a
s和
b
s,所以这两者之和。如果
a
b
相等呢。因此,
发生了33[-1,3,-4,3,4,3,-8,7,7,3]
import Control.Arrow(first, second)
import Data.List(foldl')

occurrences :: (Eq a, Integral i, Integral j, Foldable f) => a -> a -> f a -> (i, j)
occurrences a b = foldl' (flip f) (0, 0)
    where f c | a == c = first (1+)
              | b == c = second (1+)
              | otherwise = id
Prelude Control.Arrow Data.List> occurrences 3 7 [-1,3,-4,3,4,3,-8,7,7,3]
(4,2)