免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5565 | 回复: 0
打印 上一主题 下一主题

Haskell 语法参考(译) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-11 17:19 |只看该作者 |倒序浏览
本文可以被任意转载、优化修改,无须注明
译文中如有不妥之处,请参照原文
不知有没有重复造轮子,翻译中自己觉得不妥之处用 (?english?) 注明
翻译文章为:Tour of the Haskell Syntax
链接:http://cs.anu.edu.au/student/comp1100/haskell/tourofsyntax.html

译者翻译不精,译文中肯定会有许多 bugs ,还望广大 CUer 指正

Haskell 语法参考

该文档粗略的讲解 Haskell 语法,详细语法请参阅 Haskell 主页。我之所以写这个文档是因为我们这里教学
所使用的教科书(Haskell School of Expression, Paul Hudak)只是通过例子来介绍 Haskell 语言,读者可能
对这个语言没有一个整体的把握。例如,想了解“什么可以作为一个模式(pattern)”, 必须需要翻阅好几个章节

在下面的讲解中,我会用到 "..." 符号和下标,例如:

name pattern1 pattern2 ... patternn = expression        (n>=0)

这就是说,有0个或多个参数(parameter)。

有效的实例(Valid instantiations)为:

pi = 3.14159265
nextChar c = chr (ord c + 1)
simple x y z = x * (y + z)

用得最多的例子是预定义函数(Prelude functions),这就意味着,如果你想加载他们,Haskell 解释器或编译器将会
有“函数被多次定义”(functions being multiply defined)的错误

Index:

1.  Functions
2.  Pattern bindings
3.  Type signatures
4.  Guards
5.  Pattern matching
6.  Combining guards and pattern matching
7.  Patterns
8.  Expressions
9.  List comprehensions
10. Lambda expressions
11. The dot dot notation
12. Lists
13. Tuples
14. case
15. let
16. where
17. if
18. Types
19. Data types
20. Type synonyms
21. Char
22. Int
23. Integer
24. Float
25. Double
26. Bool
27. String
28. Type annotations
29. Operators
30. Parentheses
31. Quotes
32. Comments
33. Names


1. Functions(函数)

一个简单的函数定义形式为:函数名后跟一个或多个参数,等号然后是表达式
例:

simple x y z = x * (y + z)


通常函数的参数可以为任意模式(patterns)

name pattern1 pattern2 ... patternn = expression        (n>=0)

注意:在参数周围没有括号和逗号。在函数定义前,写下类型签名是一个好的习惯
在函数定义中,你可以有选择的使用 guards 和 pattern-matching

你也可以给一个表达式绑定一个模式(bind a pattern)

2. Pattern bindings(模式绑定)

不仅仅可以给一个变量绑定一个表达式,也可以给一个变量绑定一个模式(pattern),在下面的例子中选择一个匹配的模式是明智的

(xs, ys) = unzip listOfPairs
(x:y:z:rest) = [1..]

通常,模式绑定形如:

pattern = expression

另一个例子,请看 let 表达式

译者注:详情请看>>>Haskell 中的模式绑定(点击此处)<<<

3. Type signatures(类型签名)

在一个定义之前写下类型签名是一个好主意。
类型签名可以表明“提供了什么类型的参数”,可以作为注释,也可以为检测类型错误来源提供服务

一个类型签名的形式为:函数名后跟两个冒号然后是类型

simple :: Int -> Int -> Int

通常形如:

name :: type

如果函数是一个操作符,必须使用括号括住函数名:

(++) :: [a] -> [a] -> [a]

类型签名不一定要必须放在定义的上面,但大都习惯这样做
如果使用逗号来分隔函数名的话,一个签名可以定义数个函数
例:

take, drop :: Int -> [a] -> [a]

4. Guards(?导引?)

Guards 通常基于是非(bool)表达式,在函数定义中作为选择分支

-- max x y returns the largest of the two
max :: Ord a => a -> a -> a
max x y
  | x > y = x
  | otherwise = y

通常,Guards 形如:

name pattern1 pattern2 ... patternn
  | guard1 = expression1
  | guard2 = expression2
  ...
  | guardm = expressionn      (n>=0, m>=1)

guarded expressions 不是参数直接后跟等号,
而是由一个 vertical bar 后跟一个是非表达式(bool expressions),等号,然后是表达式组成
guards 按先后顺序直到第一个值为 True 的 guard,然后计算与其相关的表达式的值
otherwise 的结果永远为 True, 其没有什么特别之处,它在 Prelude 中的定义为:

otherwise :: Bool
otherwise = True

如果没有值为 True 的 guards,程序就会中止。
例:
sign :: Int -> String
sign x
  | x > 0 = "+"
  | x < 0 = "-"

在 Hugs 解释器中运行:

Main> sign 0
"
Program error: {sign 0}

Guards 可以与模式匹配(pattern-matching)组合使用。

5. Pattern matching(模式匹配)

在函数定义中,可以用模式(pattern)匹配参数:

fst :: (a, b) -> a
fst (x, y) = x

这种模式将永远成功匹配,但有的模式有可能失败
在这种情况下,连续写数个分句来共同构成一个完整的函数定义,其中每个分句形如一个函数定义

length :: [a] -> Int
length [ ] = 0
length (x:xs) = 1 + length xs

这些分句按照先后顺序来匹配,直到第一个被成功匹配。在同一时间可以用模式匹配数个参数

zip :: [a] -> [b ] -> [(a, b)]
zip [] ys = []
zip xs [] = []
zip (x:xs) (y:ys) = (x, y) : zip xs ys

如果没有一个分句被成功匹配,程序将会产生错误:

head :: [a] -> a
head (x:xs) = x

在 Hugs 解释器中运行:

Main> empty ['a','b']

Program error: {empty "ab"}

模式匹配可以与 Guards 组合使用。

6. Combining guards and pattern matching(模式匹配与 Guards 的组合)

在函数定义中可以组合使用模式匹配与 Guards:

filter :: (a -> Bool) -> [a] -> [a]
filter p [] = []
filter p (x:xs)
  | p x = x : filter p xs
  | otherwise = filter p xs

模式按照先后顺序匹配。
如果一个匹配发现有数个 Guards 必须被测试,则在第一个值为 True 的 guard 后的表达式将会被计算
如果没有 Guards 返回 True,就尝试下一个分句(如果有的话)。这一功能被应用在下面的函数定义中:

allToUpper :: [Char] -> [Char]
allToUpper (x:xs)
  | isLower x = toUpper x : allToUpper xs
allToUpper (x:xs) = x : allToUpper xs
allToUpper [] = []

即使允许,个人认为此代码混乱

7. Patterns(模式)

下表所列都可以作为模式。pattern 被缩写为 pat,constructor 被缩写为 Con。正如你所看到的,模式可以被任意的嵌套使用

模式示例描述
[ ][ ]匹配一个空列表
pat1:pat2x:xs匹配一个非空列表(第一个元素匹配
pat1,而剩余的匹配 pat2)
(pat1, pat2 ... , patn)
(n>=2)
(x, y)
(x:xs, 3, y)
匹配一个元组(各元素匹配与之相应的模式)
name x
ys
list
匹配所有的变量(绑定表达式于变量)
_
_
匹配所有情况(无绑定)
name@patl@(x:xs)
database@(name,
fields, records)
当 pat 匹配时成功匹配,并且绑定
整个表达式于name
"string""abc"
匹配给定的字符串
constant 0
'b'
3.14
匹配给定的常量
Con pat1, pat2 ... , patn
(n>=0)
Branch value left right
True
Just 3
当表达式的类型是 constructor 并且
参数匹配相应的模式时成功匹配
[pat1, pat2 ... , patn][1,2,3]
[(x,y),(3,'a')]
匹配一个列表(每个元素匹配与之相应的模式)

8. Expressions

Expression 被缩写为 expr,下表列出了一部分表达式的形式
其余形式在其他地方将被讲述:let, if-then-else, case, lists, tuples, list comprehensions, lambda

表达式示例描述
namex xs map foldl'变量,函数
constant0 'b' 3.14常量
expr expr1 expr2 ... exprnmap f xs
foldr (:) [] xs
plus 3 4
(fst funs) 5
函数应用(?function application?)
expr1 operator expr23+4
3 : list
x ^^ 2
运算符应用(?operator application?)

9. List comprehensions

List comprehensions 经常用来替代 map, filter 和 concat 来计算优雅的结果

[ x*x | x <- [1..3] ]
[ (x, y) | x < - [1..3], y <- "ab"]

vertical bar 的左边是其右边产生器产生的值的组合,每一个组合都会被计算。最右边的产生器变化最快
例如,在第二个例子中,y 的所有可能值取决与 x,这些表达式的结果如下:

[1,4,9]
[(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]

也可以在 List comprehensions 中过滤元素
其利用在产生器之间类型为 Bool 的表达式来过滤特定元素,此表达式与前面的产生器相联系

[ (x,y) | x <- [1..4], even x, y <- "ab" ]

结果为:

[(2,'a'),(2,'b'),(4,'a'),(4,'b')]

可以使用 List comprehensions 来非常优雅的定义 Prelude 函数 map 和 filter

map :: (a -> b) -> [a] -> [b ]
map f xs = [ f x | x <- xs ]
filter :: (a -> Bool) -> [a] -> [a]
filter p xs = [ x | x <- xs, p x ]

10. Lambda expressions

匿名函数可以由 lambda expressions,产生。例:

\x -> x + 1

就是
一个带有一个参数,返回这个参数加1 的结果
的函数,

lambda expressions 作为 map 函数的参数非常有用

map (\xs -> zip xs [1..]) list

lambda expressions 形如:

\pattern1 pattern2 ... patternn -> expression                (n>=1)

11. The dot dot notation

可以用".."符号来简写一个列表

表达式
结果 说明
[1..10] [1,2,3,4,5,6,7,8,9,10]
[1,4..20] [1,4,7,10,13,16,19] 指定第二个元素来改变各相邻元素间的步进
[10, 9.. 1] [10,9,8,7,6,5,4,3,2,1] 步进大小可以为负数
[2..] [2,3,4,5..] 也可以产生无限长列表
[1,3..] [1,3,5,7..]
['a'..'z'] "abcdefghijklmnopqrstuvwxyz" 所有属于 Enum 类的 type 都可以
                              
12. Lists(列表)

列表是相同类型元素的有序集合:

[3,1,4,1,59,265]        ['a', 'b']        [(3,'a'), (4,'m')]

上面的表达式的类型为:

[Int]                        [Char]                [(Int, Char)]

列表被 Haskell 认为或者为空或者为有一个头(head,第一个元素)和一个尾(tail,剩余的元素)。
使用模式匹配可以判断一个列表是否为空,如果不是,继续模式匹配列表的头和尾(pattern-matching on the head and the tail)

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs

[] 和 : 不仅可以被用作模式也可用来构造列表

map :: (a -> b) -> [a] -> [b ]
map f [] = []
map f (x:xs) = f x : map f xs

实际上,[1,2,3] 是 1:2:3:[] 的简写形式。

有几种特殊的列表,具体请参阅:String, list comprehensions 和 the dot dot notation

13. Tuples(元组)

用元组可以组合不同类型的值:

(10, 20)        ('a', 10)        ([1..3], "a string", True, 'c')

一个元组的类型说明元组各元素的类型,以上表达式的类型如下:

(Int, Int)        (Char, Int)        ([Int], String, Bool, Char)

如果一个函数返回多个值可以使用元组

headAndTail :: [a] -> (a, [a])
headAndTail (x:xs) = (x, xs)

译者注:没有一个元素的元组

14. case

使用 case 表达式可以在表达式级别来实现模式匹配的功能(而不是在函数定义级别)

length :: [a] -> Int
length xs =
  case xs of
    []     -> 0
    (x:xs) -> 1 + length xs

可以用一个表达式来匹配数个模式。

15. let

使用 let 可以定义局部函数:

sum :: Num a => [a] -> a
sum ys =
  let sum' [] total = total
      sum' (x:xs) total = sum' xs (total+x)
  in sum' ys 0

也可以方便的定义返回一个元组的递归函数,并且立即分解一个元组
(You can unwrap the tuple immediately)

unzip :: [(a, b)] -> ([a], [b ])
unzip [] = ([], [])
unzip ((a, b):rest) =
  let (as, bs) = unzip rest
  in (a:as, b:bs)

通常,一个 let 表达式形如:

let functionDefinition1
    functionDefinition2
    ...
    functionDefinitionn
in expression                        (n>=1)

局部函数的作用域是关键字 "in" 右边的表达式。"where" 是另一种定义局部函数的方法。两者的不同仅体现在作用域

16. where

使用 where 可以定义局部函数:

sum :: Num a => [a] -> a
sum ys = sum' ys 0
  where
    sum' [] total = total
    sum' (x:xs) total = sum' xs (total+x)

用 where 定义的函数仅作用于它旁边的分句

sum :: Num a => [a] -> a
sum [] = sum' [] 0 -- wrong, sum' can not be seen here!
sum ys = sum' ys 0
  where
    sum' [] total = total
    sum' (x:xs) total = sum' xs (total+x)

通常,where 语句形如:

clauseOfFunctionDefinition
  where
    functionDefinition1
     functionDefinition2
    ...
    functionDefinitionn                 (n>=1)

let 表达式是另一种定义局部函数的方法,两者的不同仅体现在作用域

17. if

在表达式级别,可以使用 if-then-else 来构造分支。下面是另一个 filter 函数的定义(请参阅 Combining guards and pattern matching)

filter :: (a -> Bool) -> [a] -> [a]
filter p [] = []
filter p (x:xs) = if p x then x : filter p xs else filter p xs

通常,if 表达式形如:

if expression1 then expression2 else expression3

expression1 必须为 Bool 类型。expression2 和 expression3 必须是同一类型
如果 expression1 为 True, 表达式就会返回 expression2 的值,否则返回 expression3 的值

当有许多分支时,用guards 更为方便
例:

kindOfChar :: Char -> String
KindOfChar c =
  if isLower c
  then "lower"
  else if isUpper c
       then "upper"
       else if isDigit c
            then "digit"
            else "other"

可以写为:

kindOfChar :: Char -> String
kindOfChar c
  | isLower c = "lower"
  | isUpper c = "upper"
  | isDigit c = "digit"
  | otherwise = "other"


18. Types(类型)

下面所列都是类型:

1.无参类型; 例,Int, Float, Char, Piece
2.元组;      例,(Int, Char)
3.列表;      例,[Int]
4.函数类型; 例,Int -> Int
5.有参类型; 例,Tree Char, Maybe q

"->"是右结合性的

19. Data types(定义类型)

在 Haskell 中,可以自行定义类型:

data Piece = Pawn | Rook | Knight | Bishop | Queen | King

Pawn, Rook 等等被称作类型 Piece 的构造函数(?constructors?)
构造函数是合法的表达式,且在函数定义中是合法的模式(pattern):

promotesTo :: Piece -> [Piece]
promotesTo Pawn = [ Rook, Knight, Bishop, Queen ]
promotesTo piece = [ piece]

构造函数可以有参数:

data Shape
  = Ellipse Float Float
  | Square Float
  | Polygon [(Float, Float)]

Ellipse 现为一个类型为 Float -> Float -> Shape 的构造函数,因此 Ellipse 3.0 4.5 是合法的表达式

数据类型可以是 parametrised(不知怎么翻译):
(Data types can be parametrised:)

data Maybe a = Just a | Nothing

Nothing 是 Maybe a 类型, Just 是 a -> Maybe a 类型,因此 Just 'a' 的类型为 Maybe Char

safeDivision :: Float -> Float -> Maybe Float
safeDivision x y = if y == 0 then Nothing else Just (x/y)

最后,数据类型可以递归定义:

data Tree a
  = Leaf a
  | Node (Tree a) (Tree a)

下面是一个计算一个 tree 所有元素总和的函数

sumTree :: Tree Int -> Int
sumTree (Leaf value) = value
sumTree (Node left right) = sumTree left + sumTree right

通常,一个数据类型的定义形如:

data TypeName typeVariable1 typeVariable2 ... typeVariablen
  = Constructor1 type1,1 type1,2 ... type1,m1
  | Constructor2 type2,1 type2,2 ... type2,m2
  ...

  | Constructorp typep,1 typep,2 ... typep,mp        (n>=0, p>=1, m1..mp >=0)

20. Type synonyms(类型别名)

使用类型别名可以给一个已存在的类型一个新的名字。String 就是一个很好的例子:

type String = [Char]

类型别名 may also be parametrised with type variables(不知怎么翻译):
(Type synonyms may also be parametrised with type variables:)

type Pair a b = (a, b)

现在 Pair Int Char 与 (Int, Char) 相同

通常,一个类型别名的定义形如:

type Name parameter1 parameter2 ... parametern = type                (n>=0)

21. Char

类型为预定义类型 Char 的值是字符。可以用单引号来引用他们:

'a' '\n' (newline) ' ' '7'

22. Int

类型为预定义类型 Int 的值是整数。但大小有限(32 bits)

Main> 100 * (100::Int)
10000
Main> 10000000 * (10000000 :: Int)
276447232

其中类型操作符是必须的。否则,Hugs 将会选择 Integer 作为数值类型
如果是计算很大的数字,应该使用 Integer 类型

用fromInt 可以把 Int 类型转化为 floating-point number(Float 和 Double)类型

23. Integer

类型为预定义类型 Integer 的值是整数。该类型的整数大小无限制

Main> 100 * (100::Integer)
10000
Main> 10000000 * (10000000 :: Integer)
100000000000000

可以用上面的例子与 Int 类型的比较一下

用 fromInteger 可以把 Integer 类型转化为 floating-point number (Float 和 Double)类型

24. Float

类型为预定义类型 Float 的值是浮点数

Main> 10 / 2.4
4.16667

用 truncate 和 round 函数可以把一个浮点数转化 Int 或 Integer 类型

25. Double

请参阅 Float, Double 精度比 Float 更高

26. Bool

尽管 Bool 类型是预定义的,但可以很容易的用 data 定义:

data Bool = True | False

Bool 表达式可以用在 if-then-else 或 guard 中。

27. String

String 类型只是 Char 列表([Char])的一个别名:

type String = [Char]

字符串有一个方便的写法。可以把

['h','e','l','l','o',' ','w','o','r','l','d']

写为:

"hello world"

也可以在模式中使用这种形式

28. Type annotations(类型注明)

可以对一个表达式注明一个类型

length [] = 0 :: Int
length (x:xs) = 1 + length xs

如果你不想经常被重载(?is overloaded?),您可能想要像例子一样使用这个
(You may want to use this if you don't want a constant to be overloaded, like in the example.)

29. Operators(操作符)

操作符是具有特殊标识符的函数。他们的标识符可包含字符 "!#$%&*+./<=>?@\^|-~ "
定义操作符时,当写下操作符的类型时,必须用括号括住

(+++) :: (a, b) -> (b, c) -> (a, b, c)
(x, y) +++ (y, z) = (x, y, z)

用括号把操作符括住可以使操作符作为一个前缀函数

(+) 3 4                (:) 3 [1,4]

相反,用反引号把一个函数括住可以使函数作为一个中缀函数

10 `mod` 3        4 `elem` listOfNumbers

请参阅 quotes

30. Parentheses(括号)

在 Haskell 中,不必使用括号括住或逗号分割参数。而它们表达的是另一个意思:

plus :: (Int, Int) -> Int
plus (x, y) = x + y

这个函数有一个被称作 pair 的参数。一般也可以定义如下:

plus :: Int -> Int -> Int
plus x y = x + y

This allows you to partially parametrise the function: (?不知怎么翻译?)  

map (plus 3) [1..10]

在函数应用(?function application?)中,如果 Haskell 不知如何解释时,可使用括号
函数应用(?function application?)的优先级最高,并且是左结合性的

map f filter g xs

上面将被看作 map 接受四个参数,这将会导致一个错误。应该如下写:

map f (filter g xs)

当参数特别复杂时,应用括号是一个非常好的举措。简单的参数有常量,变量,元组,列表和 list comprehensions

31. Quotes(?引号?)

以下是 Haskell 中所有的引号和它们代表的意思:

'a'Char 类型的值
"yada"     
String 类型的值
`mod`被作为一个运算符的函数 mod。例: 12 `mod` 5
foldl'单引号是函数名的一部分

32. Comments(注释)

单行注释以两个减号开始,一直到行尾。多行注释以 "{-" 开始, 以 "-}"结束

{- Filter selects those elements for
   which the given predicate returns True
-}
filter :: (a -> Bool) -> [a] -> [a] -- filter is polymorphic!
filter p [] = []
filter p (x:xs) = if p x then x : filter p xs else filter p xs

译者注:多行注释可以嵌套使用

33. Names(标识符)

普通的标识符(与操作符标识符相对)可以包含字母,数字,单引号 ' 和下划线 _
函数名,参数名,变量名,类型变量名首字符必须是小写字母或下划线
类型和构造函数的首字符必须是大写字母。

以下是合法的标识符:

x  foldl'  long_name  unzip3  concatMap  _yada
Bool  Cons  TreeNode  Int

更多标识符规则请参阅 operators

[ 本帖最后由 izhier 于 2009-4-29 18:48 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP