Haskell函数
函数在Haskell中起主要作用,因为Haskell是一种函数式编程语言。与其他语言一样,Haskell确实具有自己的函数定义和声明。
函数声明由函数名称,其参数列表以及其输出组成。函数定义是实际定义函数的地方。让我们看看一个添加函数的示例,以详细了解此概念。
Live Demo
add :: Integer -> Integer -> Integer --function declaration
add x y = x + y --function definition
main = do
putStrLn "The addition of the two numbers is:"
print(add 2 5) --calling a function
在这里,在第一行中声明了函数,在第二行中,我们编写了实际的函数,该函数将带有两个参数并产生一个整数类型的输出。
与大多数其他语言一样,Haskell从main
方法开始编译代码,代码将生成以下输出:
The addition of the two numbers is:
7
1. 模式匹配
模式匹配是匹配特定类型的表达式的过程。它不过是一种简化代码的技术。可以将这种技术实现为任何类型的Type
类。If-Else
可以用作模式匹配的替代选项。
模式匹配可以看作是动态多态的一种变体,在运行时,可以根据参数列表执行不同的方法。下面的代码块中使用模式匹配技术来计算数字的阶乘。
fact :: Int -> Int
fact 0 = 1
fact n = n * fact ( n - 1 )
main = do
putStrLn "The factorial of 5 is:"
print (fact 5)
我们都知道如何计算数字的阶乘。编译器将开始搜索带有参数的函数fact
。如果参数不等于0
,则数字将继续调用比实际参数小1
的相同函数。
当参数的模式与0
完全匹配时,它将调用模式fact 0 = 1
。上面代码将产生以下输出:
The factorial of 5 is:
120
2. Guards
Guards是一个与模式匹配非常相似的概念。在模式匹配中,通常匹配一个或多个表达式,但是使用Guards
来测试表达式的某些属性。
尽管建议对Guards
使用模式匹配,但是从开发人员的角度来看,Guards
更加可读和简单。对于初次使用的用户,Guards
看上去与If-Else
语句非常相似,但是它们在功能上有所不同。
在以下代码中,我们使用Guards
的概念来实现阶乘程序。
fact :: Integer -> Integer
fact n | n == 0 = 1
| n /= 0 = n * fact (n-1)
main = do
putStrLn "The factorial of 5 is:"
print (fact 5)
在这里,我们声明了两个guards
,以|
分隔并从main
调用fact
函数。在内部,编译器将以与模式匹配的方式相同的方式工作,以产生以下输出:
The factorial of 5 is:
120
3. Where字句
where
关键字或内置函数,可在运行时用于生成所需的输出。当函数计算变得复杂时,这将非常有用。
如果输入是具有多个参数的复杂表达式。在这种情况下,可以使用where
子句将整个表达式分解成小部分。
在下面的示例中,我们采用一个复杂的数学表达式来展示如何使用Haskell计算多项式方程[x ^ 2-8x + 6]
的根。
roots :: (Float, Float, Float) -> (Float, Float)
roots (a,b,c) = (x1, x2) where
x1 = e + sqrt d / (2 * a)
x2 = e - sqrt d / (2 * a)
d = b * b - 4 * a * c
e = - b / (2 * a)
main = do
putStrLn "The roots of our Polynomial equation are:"
print (roots(1,-8,6))
注意,计算给定多项式函数的根的表达式很复杂。因此,使用where
子句打断长的表达式。上面的代码将生成以下输出:
The roots of our Polynomial equation are:
(7.1622777,0.8377223)
4. 递归函数
递归是一种函数反复调用自身的情况。Haskell不提供任何多次循环任何表达式的功能。Haskell希望您将整个函数分解为不同函数的集合,并使用递归技术来实现函数。
再次考虑前面模式匹配示例,在该示例中,我们已经计算了数字的阶乘。查找数字的阶乘是使用递归的经典案例。在这里,您可能会想问:“模式匹配与递归有什么不同?”这两者之间的区别在于它们的使用方式。模式匹配用于设置终端约束,而递归是一个函数调用。
在以下示例中,同时使用了模式匹配和递归来计算阶乘5
。
Live Demo
fact :: Int -> Int
fact 0 = 1
fact n = n * fact ( n - 1 )
main = do
putStrLn "The factorial of 5 is:"
print (fact 5)
执行上面代码,得到以下结过:
The factorial of 5 is:
120
4. 高阶函数
到目前为止,我们已经看到,Haskell函数将一种类型作为输入,并产生另一种类型作为输出,这在其他命令式语言中非常相似。高阶函数是Haskell的独特功能,可以将函数用作输入或输出参数。
尽管这是一个虚拟的概念,但是在实际程序中,Haskell中定义的每个函数都使用高阶机制来提供输出。如果您有机会研究Haskell的库函数,那么就会发现大多数库函数都是以高阶编写的。
让我们以一个示例为例,在该示例中将导入一个内置的高阶函数映射,并根据选择使用该映射来实现另一个高阶函数。
import Data.Char
import Prelude hiding (map)
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map func (x : abc) = func x : map func abc
main = print $ map toUpper "yiibai.com"
在上面的示例中,我们使用了Type Char
类的toUpper
函数将输入转换为大写。在这里,方法map
将一个函数作为参数并返回所需的输出。下面是函数的输出:
sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts
sh-4.3$ main
"YIIBAI.COM"
5. Lambda表达式
有时,我们需要编写一个在应用程序的整个生命周期中只能使用一次的函数。为了应对这种情况,Haskell开发人员使用了另一个称为lambda表达式或lambda函数的匿名块。
没有定义的函数称为lambda函数。Lambda函数由\
字符表示。看看下面的一个示例,在不创建任何函数的情况下将输入值增加1
。
main = do
putStrLn "The successor of 4 is:"
print ((\x -> x + 1) 4)
在这里,创建了一个没有名称的函数(匿名函数)。它以整数4
作为参数,并输出一个值。这是一个只操作一次的函数,所以没有必要声明它,直接使用lambda表达式来实现。
上面lambda表达式将产生以下输出结果:
sh-4.3$ main
The successor of 4 is:
5