我是 Haskell 的初学者。
根据我的学校资料,函数定义中使用的约定实际上如下
function_name arguments_separated_by_spaces = code_to_do
前任 :
f a b c = a * b +c
作为一名数学学生,我习惯于使用如下函数
function_name(arguments_separated_by_commas) = code_to_do
前任 :
f(a,b,c) = a * b + c
它在 Haskell 中工作。
我的疑问是它是否适用于所有情况?
我的意思是我也可以在 Haskell 函数定义中使用传统的数学约定吗?
如果错了,在哪些特定情况下约定会出错?
提前致谢 :)
最佳答案
假设您要定义一个计算直角三角形斜边平方的函数。以下任一定义均有效
hyp1 a b = a * a + b * b
hyp2(a,b) = a * a + b * b
但是,它们的功能不一样!您可以通过查看它们在 GHCI 中的类型来判断
>> :type hyp1
hyp1 :: Num a => a -> a -> a
>> :type hyp2
hyp2 :: Num a => (a, a) -> a
以
hyp2
首先(暂时忽略 Num a =>
部分)类型告诉您该函数需要一对 (a, a)
并返回另一个 a
(例如,它可能需要一对整数并返回另一个整数,或者一对实数并返回另一个实数)。你像这样使用它>> hyp2 (3,4)
25
请注意,括号在这里不是可选的!他们确保参数是正确的类型,一对
a
s。如果你不包括它们,你会得到一个错误(现在你可能会觉得很困惑,但请放心,当你了解类型类时它会很有意义)。现在看
hyp1
一种读取类型a -> a -> a
的方法是否需要两个 a
类型的东西并返回 a
类型的其他内容.你像这样使用它>> hyp1 3 4
25
现在,如果您确实包含括号,您将收到错误消息!
所以首先要注意的是,你使用函数的方式必须与你定义它的方式相匹配。如果用括号定义函数,则每次调用时都必须使用括号。如果在定义函数时不使用括号,那么在调用它时就不能使用它们。
因此,似乎没有理由偏爱其中一种——这只是口味问题。但实际上我认为有充分的理由更喜欢一个而不是另一个,你应该更喜欢不带括号的样式。有三个很好的理由:
最后一点有点微妙。回想一下,我说过一种理解
a -> a -> a
类型函数的方法。是它需要两个 a
类型的东西, 并返回另一个 a
.但是还有另一种读取该类型的方法,即 a -> (a -> a)
.这意味着完全相同的事情,因为 ->
运算符在 Haskell 中是右结合的。解释是该函数采用单个 a
, 并返回 a -> a
类型的函数.这允许您只向函数提供第一个参数,然后再应用第二个参数,例如>> let f = hyp1 3
>> f 4
25
这在各种情况下都非常有用。例如,
map
函数允许您将某些函数应用于列表的每个元素 ->> :type map
map :: (a -> b) -> [a] -> [b]
假设你有函数
(++ "!")
这为任何 String
添加了一个爆炸.但是你有 Strings
的列表你希望他们都以一声巨响结束。没问题!您只需部分应用 map
功能>> let bang = map (++ "!")
现在
bang
是类型的函数**>> :type bang
bang :: [String] -> [String]
你可以像这样使用它
>> bang ["Ready", "Set", "Go"]
["Ready!", "Set!", "Go!"]
很实用!
我希望我已经说服您,您学校的教育 Material 中使用的约定有一些非常充分的理由被使用。作为我自己具有数学背景的人,我可以看到使用更“传统”语法的吸引力,但我希望随着您在编程之旅中的进步,您将能够看到改变为最初有点的东西的优势你不熟悉。
* 学究的注意事项 - 我知道柯里化(Currying)和部分应用并不完全相同。
** 实际上 GHCI 会告诉你类型是
bang :: [[Char]] -> [[Char]]
但自从 String
是 [Char]
的同义词这些意思是一样的。
关于function - Haskell 函数定义约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22754726/