我一直在尝试通过学习《 haskell》这本书来学习haskell。本书通过以下方式介绍了take
的实现:
take' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xs
对此代码我有两个问题:
为什么在函数声明中我们使用相同的字母i
来声明(Num i, Ord i)
?
i
是Num
类型而i
是Ord
类型的事实?
我想修改take'
,以便如果n大于列表的长度,则会显示一条消息。我尝试通过在take的定义中使用函数长度来实现此目的,但随后该函数无法加载:
take'n _
| n > length _ = print "n greater than length of list"
答案 0 :(得分:5)
为什么在函数声明中我们使用相同的字母i进行声明(数字i,奥德i)?
这是否不覆盖i属于Num且i属于Ord的事实?
这不是类型声明,而是类型 constraint 。这意味着“ i
是既是数字又是可排序的某种类型”(您可以使用不可排序的数字和可不是数字的可排序事物)。
| n>长度_ =打印“ n大于列表的长度”
这不起作用,因为_
是通配符模式-list参数实际上没有绑定任何东西。用模式写_
意味着“我知道应该去那里,但是我不在乎它是什么”。如果要使用列表,则必须为其命名。
(提示:您不需要计算列表的长度-在另一种情况下,您可以检查调用方是否尝试从列表中取出比预期多的元素)
(提示2:print "n greater than length of list"
在这里不起作用,因为它的类型为IO ()
而不是[a]
-请记住,该函数仍需要返回正确的类型!输入无效,并且要打印错误消息并终止程序,可以使用error
函数。)
答案 1 :(得分:1)
- 为什么在函数声明中我们使用相同的字母
这是否不会覆盖i
来声明(Num i, Ord i)
?i
是Num
类型而i
是Ord
类型的事实吗?
首先,让我们指出主要的误解。 Num
不是类型。 Ord
不是类型。因此,i
不能具有类型Num
或Ord
。
相反,i
代表一般的未知类型,将在调用take'
时确定。在通话时,i
可以是Int
,Integer
或Float
,仅举几例。
原则上,呼叫者可以选择他们想要的任何类型。但是,约束(Num i, Ord i)
意味着调用方必须选择满足i
(即它是数字类型)和Num i
(即它也是数字类型)的类型Ord i
。有序类型)。这两个约束条件是相辅相成的:复杂的数字是数字,但不是有序的;布尔类型是有序的,但不是数字的。
因此,这些约束之间没有矛盾。
答案 2 :(得分:1)
为什么在函数声明中我们使用相同的字母
这是否不会覆盖我的i
来声明(Num i, Ord i)
?Num
类型为i
类型为Ord
的事实?
这是类型约束。这意味着类型i
需要提供Num
(或Ord
)类型类定义的功能。如果您编写具有相同类型的多个类型类(此处为i
),则意味着i
必须是所有这些类型类的实例(并实现两个类型类都声明的功能)。对于此特定示例,这意味着i
必须是(<=) :: Ord i => i -> i -> i
和(-) :: Num i => i -> i -> i
存在的类型。如果您省略两者之一,则无法再评估n <= 0
或n-1
,因为不再保证这些函数存在。
我要修改take',以便如果n大于列表的长度,则会显示一条消息。我试图通过在take的定义中使用函数长度来实现此目的,但是函数无法加载。
Haskell的想法是print
有IO ()
。如果您写print
,您将不打印。这基本上意味着您已经{em>封装了这个IO
动作。因此,您可以将其传递出去,例如在main
中使用它来实际打印一些内容。但是,它与输出类型[a]
冲突。但是,您可以在此处使用trace
作为某种调试来执行不安全 IO
操作。对于营地:
import Debug.Trace
take' :: (Num i, Ord i) => i -> [a] -> [a]
take' n _
| n <= 0 = []
take' n [] | n > 0 = trace "n greater than length of list" []
| otherwise = []
take' n (x:xs) = x : take' (n-1) xs
但这用于调试的目的。 Haskell的想法是使用声明性语言。结果,您指定要返回的 ,希望发生的 方式不多,需要采取哪些措施。