我对 Haskell 中的函数声明感到很困惑
I'm really confused about function declarations in Haskell
这是一项家庭作业,所以我宁愿只提供提示或 link 我可以学习的地方,而不是完整的答案。这是我得到的:
allEqual :: Eq a => a -> a -> a -> Bool
据我了解,我应该比较 3 个值(在本例中为 a
、a
、a
?)和 return 是否或并非他们都彼此平等。这是我试过的:
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
我们注意到您错过了 =
而您不需要 do
。
String
的版本可能类似于
allEqual1String' :: String -> Bool
allEqual1String' x =
if x == x
then True
else False
并且我们观察到 相同的实现 适用于多种类型(Int
和 String
),前提是它们支持 ==
。
现在a
是一个类型变量,把它想象成一个变量,这样它的值就是一个类型。给定类型支持 ==
的要求在 Eq a
约束中编码(将其视为接口)。因此
allEqual :: Eq a => a -> a -> a -> Bool
适用于任何支持==
的类型。
操作方法如下:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = x == y && y == z
这是什么意思?
第一行定义函数的类型签名。
用人类的话来说,它会说这样的话:
There is a function called allEqual
for any type a
. It requires an instance of Eq a
* and takes three parameters, all of type a
, and returns a Bool
第二行说:
The function allEqual
, for any parameters x
, y
, and z
, should evaluate x == y && y == z
, which simply compares that x
equals y
and y
equals z
.
* 实例或类型 类 是许多其他编程语言所没有的语言特性,因此如果您对它们的含义感到困惑,我建议您先了解它们。
Haskell对于以前使用不同语言编程的人来说是一种有点陌生的语言。我们先来看看这个函数:
allEqual :: Int -> Int -> Int -> Bool
你可以这样看:“->
”之后的最后一个"thing"是一个return类型。预览 "things" 是参数。由此,我们知道该函数接受三个参数,即 Int
和 return 以及 Bool
。
现在看看你的函数。
allEqual :: Eq a => a -> a -> a -> Bool
有一个额外的语法“Eq a =>
”。它基本上所做的是声明中的所有以下“a
”必须实现Eq
。所以它是第一个函数的更通用的版本。它接受实现“Eq
”和returns 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
就像你的例子一样,我们有三个参数,我们 return Bool
。在第一行中,我们对所有参数调用函数 isEq3
。如果所有这些调用 return 为真,allEq3
也将 return 为真。否则,该函数将 return false。请注意,函数 isEq3
是在关键字 "where" 之后定义的。这在Haskell.
中是很常见的事情
所以我们在这里所做的是将检查所有参数是否等于 3 的大问题分解成更小的部分,检查一个值是否等于 3。
您可以大大改进此实现,但我认为这是在 Haskell 中迈出第一步的最佳方式。如果你真的想学习这门语言,你应该看看 this。
这个问题已经有其他几个很好的答案来解释如何解决你的问题。我不想那样做;相反,我将检查您的每一行代码,逐步更正问题,并希望能帮助您更好地理解 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
下一个错误是使用 do
表示法。 do
符号是出了名的容易混淆初学者,所以不要因为误用它而难过。在 Haskell 中,do
符号仅在需要逐行执行一系列语句的特定情况下使用,尤其是当您有一些副作用时(例如,打印到控制台),每行执行一次。显然,这不适合这里——您所做的只是比较一些值并 return 计算结果,这几乎不需要逐行执行。所以让我们去掉那个 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
块中使用。)
接下来还有一个问题:Bool check
无效Haskell!您可能熟悉其他语言的这种语法,但在 Haskell 中,类型总是使用 ::
指定,并且通常带有类型签名。所以我将删除名称前的 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
形式的表达式。这是多余的——你可以简单地 return 和 <condition>
具有相同的含义。所以让我们这样做:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = (x == y) == (y == z)
这比您开始使用的代码好得多!
(实际上你还可以对这段代码做一个改进。在这一点上,你的代码有一个错误应该很明显。你能找到它吗?如果是,你能修复它吗?提示:您可以使用 &&
运算符将两个布尔值“和”在一起。)
allEqual :: Eq a => a -> a -> a -> Bool
签名说:allEqual
消耗3个a
类型的值;它产生 Bool
类型的结果。 Eq a =>
部分限制了 a
可以拥有的可能操作;它说 a
是什么类型,它需要满足 Eq
中定义的要求。您可以在此处找到这些要求:http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Eq
您现在知道 a
可以做什么操作,然后您可以按照类型签名完成您的功能。
这是一项家庭作业,所以我宁愿只提供提示或 link 我可以学习的地方,而不是完整的答案。这是我得到的:
allEqual :: Eq a => a -> a -> a -> Bool
据我了解,我应该比较 3 个值(在本例中为 a
、a
、a
?)和 return 是否或并非他们都彼此平等。这是我试过的:
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
我们注意到您错过了 =
而您不需要 do
。
String
的版本可能类似于
allEqual1String' :: String -> Bool
allEqual1String' x =
if x == x
then True
else False
并且我们观察到 相同的实现 适用于多种类型(Int
和 String
),前提是它们支持 ==
。
现在a
是一个类型变量,把它想象成一个变量,这样它的值就是一个类型。给定类型支持 ==
的要求在 Eq a
约束中编码(将其视为接口)。因此
allEqual :: Eq a => a -> a -> a -> Bool
适用于任何支持==
的类型。
操作方法如下:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = x == y && y == z
这是什么意思?
第一行定义函数的类型签名。
用人类的话来说,它会说这样的话:
There is a function called
allEqual
for any typea
. It requires an instance ofEq a
* and takes three parameters, all of typea
, and returns aBool
第二行说:
The function
allEqual
, for any parametersx
,y
, andz
, should evaluatex == y && y == z
, which simply compares thatx
equalsy
andy
equalsz
.
* 实例或类型 类 是许多其他编程语言所没有的语言特性,因此如果您对它们的含义感到困惑,我建议您先了解它们。
Haskell对于以前使用不同语言编程的人来说是一种有点陌生的语言。我们先来看看这个函数:
allEqual :: Int -> Int -> Int -> Bool
你可以这样看:“->
”之后的最后一个"thing"是一个return类型。预览 "things" 是参数。由此,我们知道该函数接受三个参数,即 Int
和 return 以及 Bool
。
现在看看你的函数。
allEqual :: Eq a => a -> a -> a -> Bool
有一个额外的语法“Eq a =>
”。它基本上所做的是声明中的所有以下“a
”必须实现Eq
。所以它是第一个函数的更通用的版本。它接受实现“Eq
”和returns 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
就像你的例子一样,我们有三个参数,我们 return Bool
。在第一行中,我们对所有参数调用函数 isEq3
。如果所有这些调用 return 为真,allEq3
也将 return 为真。否则,该函数将 return false。请注意,函数 isEq3
是在关键字 "where" 之后定义的。这在Haskell.
所以我们在这里所做的是将检查所有参数是否等于 3 的大问题分解成更小的部分,检查一个值是否等于 3。
您可以大大改进此实现,但我认为这是在 Haskell 中迈出第一步的最佳方式。如果你真的想学习这门语言,你应该看看 this。
这个问题已经有其他几个很好的答案来解释如何解决你的问题。我不想那样做;相反,我将检查您的每一行代码,逐步更正问题,并希望能帮助您更好地理解 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
下一个错误是使用 do
表示法。 do
符号是出了名的容易混淆初学者,所以不要因为误用它而难过。在 Haskell 中,do
符号仅在需要逐行执行一系列语句的特定情况下使用,尤其是当您有一些副作用时(例如,打印到控制台),每行执行一次。显然,这不适合这里——您所做的只是比较一些值并 return 计算结果,这几乎不需要逐行执行。所以让我们去掉那个 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
块中使用。)
接下来还有一个问题:Bool check
无效Haskell!您可能熟悉其他语言的这种语法,但在 Haskell 中,类型总是使用 ::
指定,并且通常带有类型签名。所以我将删除名称前的 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
形式的表达式。这是多余的——你可以简单地 return 和 <condition>
具有相同的含义。所以让我们这样做:
allEqual :: Eq a => a -> a -> a -> Bool
allEqual x y z = (x == y) == (y == z)
这比您开始使用的代码好得多!
(实际上你还可以对这段代码做一个改进。在这一点上,你的代码有一个错误应该很明显。你能找到它吗?如果是,你能修复它吗?提示:您可以使用 &&
运算符将两个布尔值“和”在一起。)
allEqual :: Eq a => a -> a -> a -> Bool
签名说:allEqual
消耗3个a
类型的值;它产生 Bool
类型的结果。 Eq a =>
部分限制了 a
可以拥有的可能操作;它说 a
是什么类型,它需要满足 Eq
中定义的要求。您可以在此处找到这些要求:http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Eq
您现在知道 a
可以做什么操作,然后您可以按照类型签名完成您的功能。