【问题标题】:Haskell type mismatch Int and [Int]Haskell 类型不匹配 Int 和 [Int]
【发布时间】:2018-03-16 01:48:30
【问题描述】:

我是 Haskell 的初学者。我正在尝试创建一个具有两个参数的函数:一个字符和一个字符串。 这个函数应该遍历字符串并检查给定的字符是否在字符串中,然后返回一个整数列表,表示字符在字符串中的位置。

我的代码是:

tegnPose :: Char -> String -> [Int]
tegnPose c [] = []
tegnPose c (x:xs) = [if not (xs !! a == c)
 then [a] ++ tegnPose c xs
 else tegnPose c xs |a <- [0.. length xs - 1]]

这是一个带有列表理解的递归函数。

我得到的错误:

Uke4.hs:14:7: error:
    * Couldn't match expected type `Int' with actual type `[Int]'
    * In the expression: [a] ++ tegnPose c xs
      In the expression:
        if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
      In the expression:
        [if not (xs !! a == c) then
             [a] ++ tegnPose c xs
         else
             tegnPose c xs |
           a <- [0 .. length xs - 1]]
   |
14 |  then [a] ++ tegnPose c xs
   |       ^^^^^^^^^^^^^^^^^^^^

Uke4.hs:15:7: error:
    * Couldn't match expected type `Int' with actual type `[Int]'
    * In the expression: tegnPose c xs
      In the expression:
        if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
      In the expression:
        [if not (xs !! a == c) then
             [a] ++ tegnPose c xs
         else
             tegnPose c xs |
           a <- [0 .. length xs - 1]]
   |
15 |  else tegnPose c xs |a <- [0.. length xs - 1]]

我不明白不匹配是如何发生的,因为递归函数应该只是运行。

【问题讨论】:

  • 列表推导默认返回一个列表。尝试在其中返回另一个列表会给您一个列表列表,这不是您想要的。我会重写它以完全避免理解。

标签: haskell types int mismatch


【解决方案1】:

这就是发生不匹配的原因。首先,请注意返回 [a] 类型列表的列表推导必须生成 a 类型的元素,因此您需要以下匹配:

example :: [Int] 

--  .-- the final value is "[Int]"
--  |
example = [ 2+x*y | x <- [1..10], y <- [1..5], x < y]
--          ^^^^^
--            |
--            `- therefore, this must be "Int"

在您的示例中,tegnPose 的类型签名意味着列表推导必须返回 [Int],但表达式生成列表元素,即:

if ... then [a] ++ tegnPose c xs else tegnPose c cx

显然没有以应有的方式返回一个普通的Int

第一条错误消息表明子表达式[a] ++ tegnPos c xs 的实际类型[Int] 与整个if .. then .. else 表达式结果的预期类型不匹配,而整个if .. then .. else 表达式应该具有Int 类型。

如果我正确理解了您的问题(即,返回字符串中每个字符的整数位置列表,以便tegnPose 'a' "abracadabra" 返回[0,3,5,7,10],那么您应该或者使用递归列表推导,但不能同时使用。

注意非递归列表推导:

tegnPose c xs = [a | a <- [0..length xs - 1]

几乎做你想做的事。所缺少的只是测试条件以查看位置a 处的字符是否为c。如果您不知道在列表推导中使用“守卫”,请查看。

或者,没有列表理解的递归函数:

tegnPose c (x:xs) = if (x == c) then ??? : tegnPose c xs
                                else tegnPose c xs
tegnPose _ [] = []

几乎 做你想做的事,除了用什么代替??? 来返回一个指示当前位置的数字并不明显。如果你写一个带有额外参数的递归版本:

tp n c (x:xs) = if (x == c) then n : tp (???) c xs
                            else tp (???) c xs
tp _ _ [] = []

你可以定义的想法:

tegnPose c xs = tp 0 c xs

如果你能弄清楚n 应该用什么新值代替???,那么你会更接近。

更多标准的 Haskell 解决方案可能涉及诸如 zip 之类的东西:

> zip [0..] "abracadabra"
[(0,'a'),(1,'b'),(2,'r'),...]

和过滤器:

> filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[(0,'a'),(3,'a'),...]

和地图:

> map fst $ filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[0,3,5,7,10]

或在 Data.List 中查找符合您要求的功能:

> elemIndices 'a' "abracadabra"
[0,3,5,7,10]

【讨论】:

  • 非常感谢您的彻底回复。我发现它很有帮助 =^)
【解决方案2】:

对于某些种类,使用单个 foldr 实现此功能的更简单方法可能是;

import Data.Bool (bool)
charIndices :: Char -> String -> [Int]
charIndices c = foldr (\t r -> bool r (fst t : r) (snd t == c)) [] . zip [0..]

*Main> charIndices 't' "tektronix test and measurement instruments"
[0,3,10,13,29,34,40]

解释:

foldr 的类型是 Foldable t =&gt; (a -&gt; b -&gt; b) -&gt; b -&gt; t a -&gt; b

需要三个参数;

  1. 接受两个参数的函数
  2. b 类型的初始值
  3. 一种可遍历的数据类型,它保存a 类型的值

an 返回 b 类型的单个值。

在这种特殊情况下,我们的类型a 的值是Char 类型,这使得t a 成为String 类型(由于类型签名)并且类型b 值是整数列表[Int]

作为第一个参数提供的函数是(\t r -&gt; bool r (fst t : r) (snd t == c)),如果您检查Data.bool,则非常简单。 boola -&gt; a -&gt; Bool -&gt; a 类型的三元运算符,它接受三个参数。它们依次是否定结果、肯定结果和条件。 (负数在 Haskell 中像往常一样在左侧)。它检查当前字符是否等于我们的目标字符c,如果是,则返回fst t : r,如果不是rr 表示结果)。最后t 是馈送元组列表的当前元组。元组列表由zip [0..] s 构造,其中s 因部分应用而未在函数定义中显示。

【讨论】:

  • 这在我看来并不简单。
  • @DanielWagner 我已在我的答案中附上了解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 2021-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多