错误:实例:Num(a - > b)当我尝试获取列表元素的符号时

时间:2012-10-14 16:49:13

标签: list haskell

这就是我想要做的事情:

INPUT: [1,2,3,-1,-2,-3]
OUTPUT:[1,1,1,-1,-1,-1]

我试过了:

signNum (x:n) = map(if x>0 
            then 1 
            else -1)n

任何人都可以告诉我在逻辑中出错的地方吗?

4 个答案:

答案 0 :(得分:2)

第一个问题是map需要一个函数。所以你必须将你的if语句包装在lambda中。但是,这仍然不会完全符合您的要求。您不必将列表分为头部和尾部,而是希望将您的功能映射到整个列表中。

请记住,map只需要一个函数并将其应用于每个元素。由于您希望将每个元素转换为1-1,因此您只需要在列表中映射相应的函数。

所以最后,你得到:

sigNum ls = map (\ x -> if x > 0 then 1 else - 1) ls

答案 1 :(得分:1)

在这种情况下,可能更容易将功能分解为更小的部分。

在最低级别,可以计算单个数字的signum,即:

signum :: (Num a, Ord a) => a -> a
signum x = if x > 0 then 1 else -1

一旦你有了这个,你就可以在一个数字列表上使用它,就像你对任何函数一样:

signNum ls = map signum ls

(p.s。signum 0是什么意思?你当前的定义是signum 0 = -1

如果您需要扩展功能以包含此案例,最好使用警卫:

signum x | x < 0 = -1
         | x == 0 = 0
         | otherwise = 1

或案例陈述:

signum x = case compare x 0 of 
             LT -> -1
             EQ -> 0
             GT -> 1

答案 2 :(得分:1)

您的评论表明您希望能够理解这一点。

如何使用理解

如果你想通过理解来做到这一点,你可以做到

signNum ls = [ if x>0 then 1 else -1| x <- ls ]

如何不使用理解

......但你不能把条件放在右边

brokenSignNum ls = [ 1| x <- ls, x > 0 ]

因为在右侧放置条件会删除任何内容 不满足条件 - 你的所有否定都被忽略了!这个会 缩短列表而不是替换元素。我们试试

brokenSignNum2 ls = [ 1| x <- ls, x > 0 ] ++ [ -1| x <- ls, x <= 0 ]

这与原始列表的长度相同,但所有正面都在前面。

  

总结:您必须将此条件表达式放在左侧   因为这是唯一可以替代的地方 - 在右边它会删除。

零负?

请注意,您的if语句将0计为负数。你确定要的吗?也许你会更好地单独定义一个数字的符号:

sign x | x == 0 = 0   -- if x is zero, use zero
       | x > 0 =  1   -- use 1 for positives
       | x < 0 = -1   -- use -1 for negatives

workingSignNum1 ls = [sign x | x <- ls]

sign(几乎)与函数signum相同,所以我们也可以使用<{p>}

workingSignNum2 ls = [signum x | x <- ls]

使它更整洁

现在有很多语法基本上意味着在x列表中“sign x替换ls”。我们做了很多这样的事情,所以我们可以写一个函数来做到这一点:

replaceUsing :: (a -> b) -> [a] -> [b]
replaceUsing f xs = [f x | x <- xs]

但已经有一个功能可以做到这一点!它被称为map。所以我们可以在列表中使用地图:

quiteSlickSignNum :: Num a => [a] -> [a]
quiteSlickSignNum ls = map signum ls

甚至更光滑:

slickSignNum :: Num a => [a] -> [a]
slickSignNum = map signum

这就是我如何定义它。

为什么你说sign 几乎signum相同?

sign会收到一个数字并返回一个数字10-1,但1的类型是什么? 好吧,1的类型为Num a => a,因此您可以将其与任何数字类型一起使用。这意味着 sign接受任何类型的数字并返回任何类型的数字,因此其类型为

sign :: (Num a,Num b) => a -> b

因此我的sign版本可以为您提供不同的类型。如果您尝试一下,您会发现3 * sign 4.5为您提供3,而不是3.0,因此您可以从中获取整数,但如果您3.14 * sign 7.4你得到3.14,所以你也可以得到一个十进制类型。相比之下,

signum :: Num a => a -> a

因此它只能返回您提供的类型 - 3 * signum 4.5为您提供3.0

答案 3 :(得分:0)

错误消息“没有Num的实例”是新Haskeller解密的最棘手的问题之一。首先,这是您尝试编写的函数的完全多态类型签名(我将其添加到源文件中以获得与您相同的错误):

signNum :: (Ord a, Num a) => [a] -> [a]

查找错误

现在,编译错误消息显示:

  

无法从上下文推断(Num(a - &gt; a))(Ord a,Num a)         源自prog.hs的字面“1”:3:17

请注意,错误消息为我们提供了问题的位置。它说file_name.hs:line_number:column_number处的“文字1”是问题所在。

signNum (x:n) = map(if x>0 
            then 1 -- <-- here's the problem! (according to that message)
            else -1)n

了解错误

现在,错误消息还提示了一些可能的修复,但是每当遇到“Num没有实例”时,建议的“可能的修复”几乎总是错误的,所以忽略它们。 (我希望GHC能为Num提供更好的错误消息 - 这样的相关内容。)

回想一下错误信息:

  

无法演绎(Num(a - &gt; a))...来自字面上的'1'......

这意味着您将文字1放在上下文所期望的某种类型的地方 a -> a1显然不是函数,因此上下文错误或数字1错误。

那么文字1的上下文?

发现错误(正确)

(if x > 0
  then <<hole>>
  else -1)

如果Haskell中的语句产生一个值。 if语句的分支必须具有相同的类型,if语句的类型由分支的类型决定。

这里,另一个分支的值为-1,这是一个数字。因此,我们希望<<hole>>具有相同的类型:数字。好吧,这显然不是问题(因为1 一个数字),所以让我们看一下那个表达式的上下文。

map <<hole>> n

map函数需要函数作为其第一个参数。但是,我们知道<<hole>>会生成一个数字。找到了!这是差异:我们给map一个预期函数的数字。

纠正错误

显而易见的解决方案 - 现在我们确切地知道问题是什么和在哪里 - 是给map一个函数,而不是一个数字。有关详细信息,请参阅其他各种答案。