cycle 特别有意思,它可以无限重复列表(基于惰性计算)。听起来好像没什么用,但是在数值计算中经常需要一个包含 n 个 1 的列表,就可以使用这个方法:
1 2 3 4 5
ghci> ones n = take n (cycle [1]) ghci> ones 2 [1,1] ghci> ones 4 [1,1,1,1]
还有比如说在进行分组的时候其实特别有用:
1 2 3 4
assignToGroups n aList = zip groups aList where groups = cycle [1..n] ghci> assignToGroups 3 ["file1.txt","file2.txt","file3.txt","file4.txt","file5.txt","file6.txt","file7.txt","file8.txt"] [(1,"file1.txt"),(2,"file2.txt"),(3,"file3.txt"),(1,"file4.txt"),(2,"file5.txt"),(3,"file6.txt"),(1,"file7.txt"),(2,"file8.txt")]
递归和模式匹配
递归实现一个欧几里得算法(最大公约数):
1 2 3 4
myGCD a b = if remainder == 0 then b else myGCD b remainder where remainder = a `mod` b
可以看到 Haskell 写算法的优雅了吧!几乎就是欧几里得算法的直接表达:
求 a 和 b 的最大公约数,如果余数为 0,那么就是 b,否则最大公约数就是 b 与 余数的最大公约数。
ghci> :info Num typeNum :: * -> Constraint classNum a where (+) :: a -> a -> a (-) :: a -> a -> a (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-} -- Defined in ‘GHC.Num’ instanceNumWord-- Defined in ‘GHC.Num’ instanceNumInteger-- Defined in ‘GHC.Num’ instanceNumInt-- Defined in ‘GHC.Num’ instanceNumFloat-- Defined in ‘GHC.Float’ instanceNumDouble-- Defined in ‘GHC.Float’
使用 Num 作为函数的参数,那么只要满足 Num 定义的类都可以传入(可以看到 Go 语言的接口借鉴了这种实现):
1 2
addThenDouble :: (Num a) => a -> a -> a addThenDouble x y = (x + y) * 2
使用 Num 这个类型类后,代码支持的类型就不仅仅只是 Int 和 Double 了,也包括所有实现了 Num 的类,无论是内置的还是第三方开发者写的。
定义类型类
可以通过 where 定义类需要拥有的方法,例如我们可以设计一个 describe 方法,用来描述类型,那么就可以使用:
1 2
classDescribable a where describe :: a -> String
通用的基础类型类
比如在排序算法中,我们必须要有一个方法对两个值进行比较才可以排序。像 C++ 中,我们可能会重载 < 符号,实现自定义的排序。这些我们可以理解成类型的基础能力。例如 数字 是可以比较大小的,描述支持比较,用的就是 Ord 和 Eq:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classEq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool classEq a => Ord a where compare :: a -> a -> Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool max :: a -> a -> a min :: a -> a -> a ghci> :t (>) (>) :: Ord a => a -> a -> Bool
不难从逻辑上发现实现 Ord 必须实现 Eq,我们需要知道两者是否相等才可以进一步说其中一个值是否比另一个大。然而,实现 Eq 却未必可以比较。比如你可以判断香草冰淇淋和巧克力的异同 Eq,却无法将它两者进行直接比较 Ord。
如果通过 :info 比较 Int 和 Integer 这两个类型的区别,就会发现 Int 实现了 Bounded,而 Integer 没有实现 Bounded。
ghci> [S1 .. S6] [one,two,three,four,five,six] ghci> [S1 .. ] [one,two,three,four,five,six,*** Exception: No such value CallStack (from HasCallStack): error, called at hello.hs:102:14in main:Main