提问人:Nico Arevalo 提问时间:4/30/2020 最后编辑:Will NessNico Arevalo 更新时间:4/30/2020 访问量:603
我对Haskell中的函数声明感到非常困惑
I'm really confused about function declarations in Haskell
问:
这是一份家庭作业,所以我更喜欢只有提示或我可以学习的链接,而不是完整的答案。这是我得到的:
allEqual :: Eq a => a -> a -> a -> Bool
我从中了解到的是,我应该比较 3 个值(在本例中为 , , ?) 并返回它们是否都彼此相等。这是我尝试过的:a
a
a
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
老实说,我对 Haskell 感到完全迷茫,所以任何关于如何阅读函数或声明函数的提示都会有很大帮助。
答:
让我们从一个函数开始,该函数采用单个:Int
allEqual1Int :: Int -> Bool
allEqual1Int x = True
allEqual1Int' :: Int -> Bool
allEqual1Int' x =
if x == x
then True
else False
如果我们将其与您的生产线进行比较
allEqual x y z do
我们注意到您缺少 A,并且您不需要 .=
do
的版本可能看起来像String
allEqual1String' :: String -> Bool
allEqual1String' x =
if x == x
then True
else False
我们观察到,相同的实现适用于多种类型(和),只要它们支持 。Int
String
==
现在是一个类型变量,把它想象成一个变量,这样它的值就是一个类型。给定类型支持的要求被编码在 Eq a
约束中(将其视为接口)。因此a
==
allEqual :: Eq a => a -> a -> a -> Bool
适用于任何支持 .==
操作方法如下:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = x == y && y == z
这是什么意思?
第一行定义函数的类型签名。
用人类的话来说,它会说这样的话:
任何类型都有一个函数调用。它需要一个 * 的实例,并接受三个参数,所有参数的类型都是 ,并返回一个
allEqual
a
Eq a
a
Bool
第二行说:
函数 ,对于任何参数 , , 和 ,都应该计算 ,它只是比较等于和等于 。
allEqual
x
y
z
x == y && y == z
x
y
y
z
* 实例或类型类是一种语言特性,没有多少其他编程语言所具有,所以如果你对它们的含义感到困惑,我建议你先了解它们。
评论
对于那些以前用不同语言编程的人来说,Haskell是一门有点奇怪的语言。我们先来看看这个函数:
allEqual :: Int -> Int -> Int -> Bool
你可以这样看:“”后面的最后一个“东西”是返回类型。预览“事物”是参数。由此,我们知道该函数接受三个参数,这些参数是并返回 .->
Int
Bool
现在看看你的函数。
allEqual :: Eq a => a -> a -> a -> Bool
还有一个额外的语法 “”。它基本上所做的是声明中必须实现的以下所有“”。因此,它是第一个函数的更通用版本。它接受实现 “” 的三个参数并返回 .该函数可能应该做的是检查所有值是否相等。Eq a =>
a
Eq
Eq
Bool
现在让我们看一下您的实现。您使用的是 do 语法。我觉得一开始这不是最好的方法。让我们实现一个非常相似的函数,它检查所有参数是否都等于 3。
allEq3 :: Int -> Int -> Int -> Bool
allEq3 x y z = isEq3 x && isEq3 y && isEq3 z
where
isEq3 :: Int -> Bool
isEq3 x = x == 3
就像在您的示例中一样,我们有三个参数,我们返回 .在第一行中,我们对所有参数调用函数。如果所有这些调用都返回 true,也将返回 true。否则,该函数将返回 false。请注意,该函数在关键字“where”之后定义。这在Haskell中很常见。Bool
isEq3
allEq3
isEq3
因此,我们在这里所做的是解决一个大问题,即检查所有参数是否都等于 3,并将其分成更小的部分,检查值是否等于 3。
你可以对这个实现进行很多改进,但我认为这是在 Haskell 中迈出第一步的最佳方式。如果你真的想学习这门语言,你应该看看这个。
这个问题已经有其他几个非常好的答案来解释如何解决你的问题。我不想那样做;相反,我将遍历您的代码的每一行,逐步纠正问题,并希望能帮助您更好地理解 Haskell。
首先,为了方便起见,我将复制您的代码:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
第一行是类型签名;这在其他答案中已经很好地解释了,所以我将跳过这一点并继续下一行。
第二行是定义函数的位置。您忽略的第一件事是需要一个等号来定义函数:函数定义语法是 ,并且您不能删除 .因此,让我们纠正一下:functionName arg1 arg2 arg3 … = functionBody
=
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = do
Bool check <- x == y
Bool nextC <- y == z
if check == nextC
then True
else False
下一个错误是使用表示法。 符号因混淆初学者而臭名昭著,所以不要为滥用它而感到难过。在 Haskell 中,符号只在需要逐行执行一系列语句的特定情况下使用,尤其是当你有一些副作用(比如,打印到控制台)时,每行都会执行。显然,这不适合这里——你所做的只是比较一些值并返回一个结果,这几乎不需要逐行执行。所以让我们摆脱它:do
do
do
do
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let Bool check = x == y
Bool nextC = y == z
in
if check == nextC
then True
else False
(我还用 替换了绑定,因为只能在块内使用。<-
let … in …
<-
do
接下来,另一个问题:Haskell 无效!您可能熟悉其他语言的这种语法,但在 Haskell 中,类型总是使用 指定,并且通常使用类型签名。因此,我将删除名称之前的内容,并添加类型签名:Bool check
::
Bool
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let check :: Bool
check = x == y
nextC :: Bool
nextC = y == z
in
if check == nextC
then True
else False
现在,在这一点上,你的程序是完全有效的Haskell——你将能够编译它,并且它会工作。但是,您仍然可以进行一些改进。
首先,你不需要包含类型——Haskell具有类型推断功能,在大多数情况下,省略类型是可以的(尽管传统上将它们包含在函数中)。因此,让我们去掉 :let
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
let check = x == y
nextC = y == z
in
if check == nextC
then True
else False
现在,并且只在一个地方使用 - 给它们命名不会做任何事情,只会降低代码的可读性。因此,我将内联其用法的定义和用法:check
nextC
check
nextC
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z =
if (x == y) == (y == z)
then True
else False
最后,我看到你有一个形式的表达式。这是多余的——你可以简单地返回具有相同含义的 。因此,让我们这样做:if <condition> then True else False
<condition>
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = (x == y) == (y == z)
这比你开始使用的代码要好得多!
(实际上,您可以对此代码进行另一项改进。在这一点上,你的代码应该很明显有一个错误。你能找到它吗?如果是这样,你能修复它吗?提示:您可以使用运算符将两个布尔值“和”在一起。&&
评论
do
do
do
allEqual :: Eq a => a -> a -> a -> Bool
签名说:消耗 3 个类型的值;它生成 类型的结果。该部分限制了可能的操作;它说无论类型是什么,它都需要满足 中定义的要求。您可以在此处找到这些要求: http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Eq 您现在知道了操作可以执行的操作,然后可以通过遵循类型签名来完成函数。allEqual
a
Bool
Eq a =>
a
a
Eq
a
评论
a
评论
x=1
y=2
z=3
<-
x
y
y
z
if fact then True else False
fact
if fact then False else True
not fact