Haskell函数定义约定

han*_*ugm 6 haskell function

我是Haskell的初学者.

根据我的学校资料在功能定义中使用的惯例实际上如下

function_name arguments_separated_by_spaces = code_to_do

例如:

f a b c = a * b +c
Run Code Online (Sandbox Code Playgroud)

作为一名数学学生,我习惯于使用如下的功能

function_name(arguments_separated_by_commas)= code_to_do

例如:

f(a,b,c) = a * b + c
Run Code Online (Sandbox Code Playgroud)

它在Haskell工作.

我怀疑它是否适用于所有情况?

我的意思是我可以在Haskell函数定义中使用传统的数学约定吗?

如果错了,在哪些特定情况下会出错?

提前致谢 :)

Chr*_*lor 13

假设您要定义一个函数来计算右三角形的hypoteneuse的平方.以下任一定义均有效

hyp1 a b = a * a + b * b

hyp2(a,b) = a * a + b * b
Run Code Online (Sandbox Code Playgroud)

但是,它们的功能不一样!您可以通过查看GHCI中的类型来判断

>> :type hyp1
hyp1 :: Num a => a -> a -> a

>> :type hyp2
hyp2 :: Num a => (a, a) -> a
Run Code Online (Sandbox Code Playgroud)

hyp2第一(和忽略Num a =>的部分现在)类型告诉你的函数取一对(a, a)并返回另一个a(例如,它可能需要两个整数并返回另一个整数,或一对实数和返回另一个实数).你这样使用它

>> hyp2 (3,4)
25
Run Code Online (Sandbox Code Playgroud)

请注意,括号在这里不是可选的!他们确保参数的类型正确,一对as.如果你不包含它们,你会收到一个错误(现在看起来可能会让你感到困惑,但请放心,当你学会了类型类时它会有意义).

现在看hyp1一种读取类型的方法a -> a -> a是它需要两种类型的a东西并返回其他类型的东西a.你这样使用它

>> hyp1 3 4
25
Run Code Online (Sandbox Code Playgroud)

现在,如果你,你会得到一个错误包括括号!

所以首先要注意的是,使用该函数的方式必须与您定义它的方式相匹配.如果使用parens定义函数,则每次调用它时都必须使用parens.如果在定义函数时不使用parens,则在调用它时不能使用它们.

因此,似乎没有理由偏爱另一个 - 这只是一个品味问题.但实际上,我认为有一个很好的理由,更喜欢一个比其他的,你应该更喜欢的风格没有括号.有三个很好的理由:

  1. 它看起来更干净,如果你没有让页面混乱的话,你的代码也更容易阅读.

  2. 如果你在任何地方都使用parens,你将会受到性能影响,因为每次使用该函数时都需要构造和解构一对(尽管编译器可能会优化它 - 我不确定).

  3. 你想获得currying的好处,也就是部分应用的函数*.

最后一点有点微妙.回想一下,我说理解类型函数的一种方法a -> a -> a是它需要两种类型的东西a,然后返回另一种类型a.但还有另一种方式来阅读这种类型,即a -> (a -> a).这意味着完全相同的事情,因为->运算符在Haskell中是正确关联的.解释是该函数采用单个函数a,并返回一个类型的函数a -> a.这允许您只为函数提供第一个参数,并稍后应用第二个参数,例如

>> let f = hyp1 3
>> f 4
25
Run Code Online (Sandbox Code Playgroud)

这在各种情况下实际上都是有用的.例如,map函数允许您将某些函数应用于列表的每个元素 -

>> :type map
map :: (a -> b) -> [a] -> [b]
Run Code Online (Sandbox Code Playgroud)

假设你有(++ "!")任何功能可以增加任何一个爆炸声String.但你有列表,Strings并且你希望它们都以爆炸结束.没问题!您只是部分应用该map功能

>> let bang = map (++ "!")
Run Code Online (Sandbox Code Playgroud)

现在bang是**型的功能

>> :type bang
bang :: [String] -> [String]
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它

>> bang ["Ready", "Set", "Go"]
["Ready!", "Set!", "Go!"]
Run Code Online (Sandbox Code Playgroud)

非常有用!

我希望我已经说服你,你学校教育材料中使用的惯例有一些非常可靠的理由被使用.作为一个拥有数学背景的人,我可以看到使用更"传统"语法的吸引力,但我希望随着你在编程过程中的进步,你将能够看到改变为最初有点的东西的优势对你不熟悉


*对于学生的注意事项 - 我知道currying和部分应用并不完全相同.

**实际上GHCI会告诉你类型,bang :: [[Char]] -> [[Char]]但因为这String是同义词的同义词[Char].

  • @Xeo他们有点相关.curried/tupled表单形成[adjunction](http://en.wikipedia.org/wiki/Adjoint_functors),其中F是`( - x B)`,G是` - ^ B`. (2认同)