这就是我想要做的事情:
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
任何人都可以告诉我在逻辑中出错的地方吗?
答案 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
会收到一个数字并返回一个数字1
,0
或-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 -> a
。 1
显然不是函数,因此上下文错误或数字1错误。
那么是文字1的上下文?
发现错误(正确)
(if x > 0
then <<hole>>
else -1)
如果Haskell中的语句产生一个值。 if语句的分支必须具有相同的类型,if语句的类型由分支的类型决定。
这里,另一个分支的值为-1
,这是一个数字。因此,我们希望<<hole>>
具有相同的类型:数字。好吧,这显然不是问题(因为1 是一个数字),所以让我们看一下那个表达式的上下文。
map <<hole>> n
map
函数需要函数作为其第一个参数。但是,我们知道<<hole>>
会生成一个数字。找到了!这是差异:我们给map
一个预期函数的数字。
纠正错误
显而易见的解决方案 - 现在我们确切地知道问题是什么和在哪里 - 是给map
一个函数,而不是一个数字。有关详细信息,请参阅其他各种答案。