Haskell笔记 (19)—— Guard

guard跟在函数名和参数之后,用管道符号表示:

myCompare :: (Ord a) => a -> a -> Ordering  

myCompare a b
    | a < b = LT
    | a == b = EQ
    | otherwise = GT    

guard长得和pattern类似,pattern检查输入是否符合这个pattern,而guard则会计算布尔表达式,返回TrueFalse。如果返回值是True,则执行等号后面的函数体部分。

看另外一个例子:

Capture

The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern.

guard没有otherwise,如果都不匹配的话,就会执行下一个pattern

参考资料:
Guards, guards!

Haskell笔记 (17)—— Value和type

Because Haskell is a purely functional language, all computations are done via the evaluation of expressions (syntactic terms) to yield values (abstract entities that we regard as answers). Every value has an associated type. (Intuitively, we can think of types as sets of values.) Examples of expressions include atomic values such as the integer 5, the character ‘a’, and the function \x -> x+1, as well as structured values such as the list [1,2,3] and the pair (‘b’,4).

Just as expressions denote values, type expressions are syntactic terms that denote type values (or just types). Examples of type expressions include the atomic types Integer (infinite-precision integers), Char (characters), Integer->Integer (functions mapping Integer to Integer), as well as the structured types [Integer] (homogeneous lists of integers) and (Char,Integer) (character, integer pairs).

All Haskell values are “first-class”—they may be passed as arguments to functions, returned as results, placed in data structures, etc. Haskell types, on the other hand, are not first-class. Types in a sense describe values, and the association of a value with its type is called a typing. Using the examples of values and types above, we write typings as follows:

                      5  :: Integer
                     'a' :: Char
                     inc :: Integer -> Integer
                 [1,2,3] :: [Integer]
                 ('b',4) :: (Char,Integer)

The “::” can be read “has type.”

Haskell是一门纯函数式编程语言,所有的计算都是通过对表达式的求值完成的。每个值(value)都有一个相关的类型(type),也可以把类型看做是值的集合。在Haskell中,所有的值都是first-class,而类型却不是。类型和值的集合称之为typing

5  :: Integer

 

Haskell笔记 (16)—— Currying

Currying定义如下:

Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.

Haskell中所有函数都可以认为是curried,即函数只包含一个参数。在Haskell类型表示中,->是右相关的,因此f :: a -> b -> c实际上也是f :: a -> ( b -> c ),所以f x y(f x) y

参考资料:
Currying

下面参考自Curried functions

Every function in Haskell officially only takes one parameter. So how is it possible that we defined and used several functions that take more than one parameter so far? Well, it’s a clever trick! All the functions that accepted several parameters so far have been curried functions.

Putting a space between two things is simply function application. The space is sort of like an operator and it has the highest precedence.

So how is that beneficial to us? Simply speaking, if we call a function with too few parameters, we get back a partially applied function, meaning a function that takes as many parameters as we left out. Using partial application (calling functions with too few parameters, if you will) is a neat way to create functions on the fly so we can pass them to another function or to seed them with some data.

关于curry infix function(需要加括号):

Infix functions can also be partially applied by using sections. To section an infix function, simply surround it with parentheses and only supply a parameter on one side. That creates a function that takes one parameter and then applies it to the side that’s missing an operand. An insultingly trivial function:

divideByTen :: (Floating a) => a -> a  
divideByTen = (/10) 

The only special thing about sections is using -. From the definition of sections, (-4) would result in a function that takes a number and subtracts 4 from it. However, for convenience, (-4) means minus four. So if you want to make a function that subtracts 4 from the number it gets as a parameter, partially apply the subtract function like so: (subtract 4).

 

Haskell笔记 (15)—— Type annotation

当需要显示地指定表达式类型时,可以使用type annotaion,即在表达式后面加上::和需要指定的类型。举例如下:

> :type 1 :: Int
1 :: Int :: Int

可以看到1被强制指定为Int类型。Type annotation也可以用来获得Int等类型的边界值:

> minBound :: Int
-9223372036854775808
> maxBound :: Int
9223372036854775807

参考资料:
Lecture Notes on Haskell Programming

 

Haskell笔记 (14)—— List comprehension

在数学中,comprehension可以表示为从一个集合生成另一个集合:

{x²  |  x ∈ {1...5}}

Haskell中,list comprehension可以表示从一个list生成另外一个list

> [x^2 | x <- [1 .. 10], even x]
[4,16,36,64,100]

list comprehension可以包含两部分:x <- [1 .. 10]generator,表明x的值从哪里获得;even xguard,相当于限制哪些x的值可以用于生成新的listGeneratorguard都可以有多个,用,分开:

> [x * y | x <- [1 .. 10], y <- [1 .. 10], even x, odd y]
[2,6,10,14,18,4,12,20,28,36,6,18,30,42,54,8,24,40,56,72,10,30,50,70,90]

需要注意的是,改变generator的顺序会改变最后生成list的顺序。多个generator像嵌套循环,位置靠后的是里层循环,位置靠前的是外层循环。举例如下:

> [(x, y) | x <- [1, 2, 3], y <- [4, 5]]
[(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
> [(x, y) | y <- [4, 5], x <- [1, 2, 3]]
[(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)]

参考资料:
List Comprehensions

 

Haskell笔记 (12)—— 定义新的数据类型(data type)

使用data关键字来定义一种新的数据类型(data type):

data StudentInfo = Student Int String
                    deriving (Show)

StudentInfotype constructor,也是这种新type的名字,所以首字母必须大写。

Studentvalue constructor(有时也称之为data constructor)的名字,它的首字母也必须大写(可以把它看做是首字母大写的函数),作用是用来创建StudentInfo这种type的值。在Student后面的IntStringStudentInfo这种typecomponent(有时也称作fieldparameter,其作用类似于其它编程语言中结构体成员。

type constructor只用在type declarationtype signature中,而value constructor只用在实际的代码中。因此二者的使用是相互独立的,可以让type constructorvalue constructor拥有相同的名字,实际编码中基本也是这样做的:

data Student = Student Int String
                        deriving (Show)

也可以使用type关键字为已有的类型定义一个同义词(type synonym):

type SI = StudentInfo

type synonym也可以为一个冗长的类型取一个短名字,举例如下:

type SI = (Int, String)

Haskell笔记 (11)—— 函数的“type signature”

看一下take函数的type signature

ghci> :type take
take :: Int -> [a] -> [a]

这表明它有两个参数,返回值是一个list。“->”是右相关(right-associative),因此实际上也可以把take函数的type signature写成这样:

ghci> :type take
take :: Int -> ([a] -> [a])

因此也可以这样理解take函数:它有一个Int参数,返回值是另一个函数。这个函数的参数和返回类型是同一个类型的list

 

Haskell笔记 (10)—— Lazy evaluation

Haskell中对表达式的计算使用的是Lazy evaluation(也称之为nonstrict evaluation):即只有表达式的值真正需要时,才会被计算。Haskell中把用来跟踪还没有计算的表达式的记录称之为trunk

参考自stackoverflow

Mostly because it can be more efficient — values don’t need to be computed if they’re not going to be used. For example, I may pass three values into a function, but depending on the sequence of conditional expressions, only a subset may actually be used. In a language like C, all three values would be computed anyway; but in Haskell, only the necessary values are computed.

It also allows for cool stuff like infinite lists. I can’t have an infinite list in a language like C, but in Haskell, that’s no problem. Infinite lists are used fairly often in certain areas of mathematics, so it can be useful to have the ability to manipulate them.

Lazy evaluation的优点:高效(延迟执行代码),支持像infinite lists这样的cool stuff