文章目录

  • Function
    • Pattern Matching
    • Guards
    • Where
    • Let it be
    • Case
  • Recursion
  • Types
    • Int
    • Integer
    • Float
    • Double
    • Bool
    • Char
  • TypeClasses
    • Eq
    • Ord
    • Show
    • Read
    • Enum
    • Bounded
    • Num
    • Integral
    • Floating
  • if
  • List
    • Functions on Lists
    • List Comprehension
  • Tuple
  • Higher Order Functions
    • Curried functions
    • Maps & filters
    • Lambdas
    • Only folds and horses
    • Function application with \$
    • Function composition
  • Modules
    • Loading Modules
    • Data.List
      • intersperse
      • intercalate
      • transpose
      • concat
      • concatMap
      • and
      • or
      • any & all
      • iterate
      • splitAt
      • takeWhile
      • dropWhile
      • span
      • break
      • sort
      • group
      • inits & tails
      • isInfixOf & isPrefixOf & isSuffixOf
      • elem & notElem
      • partition
      • find
    • Data.Char
    • Data.Map
      • fromList
      • empty
      • insert
      • null
      • size
      • singleton
      • lookup
      • member
      • map & filter
      • toList
      • keys & elems
    • Data.Set
      • fromList
      • intersection
      • difference
      • union
      • null & size & member & empty & singleton & insert & delete
    • Making our own modules
  • Making Our Own Types and Typeclasses
    • Record syntax
    • Type parameters
    • Derived instances
    • Type synonyms
  • Input & Output
    • Files and Streams
  • Functionally Solving Problems
    • Reverse Polish notation calculator

Function

in_range min max x =min <= x && x <= max

Pattern Matching

Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

When defining functions, you can define separate function bodies for different patterns. This leads to really neat code that’s simple and readable. You can pattern match on any data type — numbers, characters, lists, tuples, etc.

is_zero 0 = True
is_zero _ = False

Guards

Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it’s very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.

Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.+

fac n | n <= 1 = 1 | otherwise = n * fac (n - 1)

Where

in_range min max x = ilb && iubwhereilb = min <= xiub = max >= x

Let it be

in_range min max x = let in_lower_bound = min <= xin_upper_bound = max >= xinin_lower_bound && in_upper_boundghci> [let square x = x * x in (square 5, square 3, square 2)]  -- [(25,9,4)] ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100  -- 600  ghci> let boot x y z = x * y + z in boot 3 4 2  -- 14
ghci> boot  -- <interactive>:1:0: Not in scope: `boot'

Case

case expression of pattern -> result  pattern -> result  pattern -> result  ...

Recursion

Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively.

Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

Pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions.

-- 阶乘
fac n =if n < 1 then1elsen * fac (n - 1)-- 列表中的最大值
maximum' :: (Ord a) => [a] -> a
maximum' [] = error "maximum of empty list"
maximum' [x] = x
maximum' (x:xs)  -- (x:xs) a very common idiom when doing recursion with lists| x > maxTail = x| otherwise = maxTailwhere maxTail = maximum' xs

Types

Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code.

:: is read as “has type of”.

in_range :: Integer -> Integer -> Integer -> Bool

Int

Int stands for integer. It’s used for whole numbers. 7 can be an Int but 7.2 cannot. Int is bounded, which means that it has a minimum and a maximum value. Usually on 32-bit machines the maximum possible Int is 2147483647 and the minimum is -2147483648.

Integer

Integer stands for, er … also integer. The main difference is that it’s not bounded so it can be used to represent really really big numbers. I mean like really big. Int, however, is more efficient.

Float

Float is a real floating point with single precision.

Double

Double is a real floating point with double the precision!

Bool

Bool is a boolean type. It can have only two values: True and False.

Char

Char represents a character. It’s denoted by single quotes. A list of characters is a string.

TypeClasses

A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better.

Eq

Eq is used for types that support equality testing. The functions its members implement are == and /=. So if there’s an Eq class constraint for a type variable in a function, it uses == or /= somewhere inside its definition. All the types we mentioned previously except for functions are part of Eq, so they can be tested for equality.

Ord

Ord is for types that have an ordering.

Show

Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.

Read

Read is sort of the opposite typeclass of Show. The read function takes a string and returns a type which is a member of Read.

Enum

Enum members are sequentially ordered types — they can be enumerated. The main advantage of the Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecesors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.

Bounded

Bounded members have an upper and a lower bound.

Num

Num is a numeric typeclass. Its members have the property of being able to act like numbers. Let’s examine the type of a number.

Integral

Integral is also a numeric typeclass. Num includes all numbers, including real numbers and integral numbers, Integral includes only integral (whole) numbers. In this typeclass are Int and Integer.

Floating

Floating includes only floating point numbers, so Float and Double.

if

in_range min max x = if ilb then iub else Falsewhereilb = min <= xiub = max >= x

List

[1, 2, 3, 4, 5] :: [Integer]-- List 相加
ghci> [1,2,3,4] ++ [9,10,11,12]  -- 在头部插入
ghci> 5:[1,2,3,4,5]  -- [5,1,2,3,4,5]  -- 索引
ghci> [1, 2, 3, 4, 5] !! 3  -- 4-- 列表比较,从前往后比:若相等则比较下一个;否则返回比较结果
ghci> [3,2,1] > [2,1,0]  -- True
ghci> [3,2,1] > [2,10,100]  -- True
ghci> [3,4,2] > [3,4]  -- True
ghci> [3,4,2] > [2,4]  -- True
ghci> [3,4,2] == [3,4,2]  -- True  [1, 3..100]
asc :: Int -> Int -> [Int]
asc n m| m < n = []| m == n = [m]| m > n = n : asc (n + 1)

Functions on Lists

  • head - the first element of the list

    -- head takes a list and returns its head. The head of a list is basically its first element.
    head :: [a] -> a
    
  • tail - the list of elements without the first one

    -- tail takes a list and returns its tail. In other words, it chops off a list's head.
    tail :: [a] -> [a]
    
  • last - the last element

    -- last takes a list and returns its last element.
    ghci> last [5,4,3,2,1]  -- 1
    
  • length - the length of the list

    length :: [a] -> Int
    
  • init - the list of elements without the last one

    -- init takes a list and returns everything except its last element.
    init :: [a] -> [a]
    
  • null - the list is empty or not

  • reverse

    -- reverse reverses a list.
    ghci> reverse [5,4,3,2,1]  -- [1,2,3,4,5]
    
  • take

    -- take takes number and a list. It extracts that many elements from the beginning of the list. Watch.
    ghci> take 3 [5,4,3,2,1]  -- [5,4,3]
    ghci> take 1 [3,9,3]  -- [3]
    ghci> take 5 [1,2]  -- [1,2]
    ghci> take 0 [6,6,6]  -- []
    
  • drop

    -- drop works in a similar way, only it drops the number of elements from the beginning of a list.
    
  • maximum / minimum

  • sum

  • product

  • elem

    -- elem takes a thing and a list of things and tells us if that thing is an element of the list. It's usually called as an infix function because it's easier to read that way.
    
  • cycle

    -- cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.
    ghci> take 10 (cycle [1,2,3])  -- [1,2,3,1,2,3,1,2,3,1]
    
  • repeat

    -- repeat takes an element and produces an infinite list of just that element. It's like cycling a list with only one element.
    ghci> take 10 (repeat 5)  -- [5,5,5,5,5,5,5,5,5,5]
    
  • replicate

    ghci> replicate 3 10  -- [10,10,10]
    

List Comprehension

ghci> [x*2 | x <- [1..10]]  -- [2,4,6,8,10,12,14,16,18,20]  ghci> [x*2 | x <- [1..10], x*2 >= 12]  -- [12,14,16,18,20]ghci> [ x | x <- [50..100], x `mod` 7 == 3]  -- [52,59,66,73,80,87,94]   ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]  -- [55,80,100,110]

Tuple

-- fst
-- fst takes a pair and returns its first component.
ghci> fst (8,11)  -- 8
ghci> fst ("Wow", False)  -- "Wow"  -- snd
-- snd takes a pair and returns its second component. Surprise!
ghci> snd (8,11)  -- 11
ghci> snd ("Wow", False)  -- False  -- zip
-- It takes two lists and then zips them together into one list by joining the matching elements into pairs.
-- It's a really simple function but it has loads of uses.
-- It's especially useful for when you want to combine two lists in a way or traverse two lists simultaneously.
ghci> zip [1,2,3,4,5] [5,5,5,5,5]  -- [(1,5),(2,5),(3,5),(4,5),(5,5)]
ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]  -- [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]

Higher Order Functions

Haskell functions can take functions as parameters and return functions as return values. A function that does either of those is called a higher order function.

Higher order functions aren’t just a part of the Haskell experience, they pretty much are the Haskell experience. It turns out that if you want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them, higher order functions are indispensable. They’re a really powerful way of solving problems and thinking about programs.

Curried functions

All the functions that accepted several parameters so far have been curried functions.

compareWithHundred :: (Num a, Ord a) => a -> Ordering
-- compareWithHundred x = compare 100 x
-- 第二行的代码可以被重写为第四行,因为 compare 100 返回了一个 “输入一个数,并与 100 比较” 的函数
compareWithHundred = compare 100

Maps & filters

Mapping and filtering is the bread and butter of every functional programmer’s toolbox.

map takes a function and a list and applies that function to every element in the list, producing a new list.

ghci> map (+3) [1,5,3,1,6]  -- [4,8,6,4,9]
ghci> map (++ "!") ["BIFF", "BANG", "POW"]  -- ["BIFF!","BANG!","POW!"]
ghci> map (replicate 3) [3..6]  -- [[3,3,3],[4,4,4],[5,5,5],[6,6,6]]

filter is a function that takes a predicate (a predicate is a function that tells whether something is true or not, so in our case, a function that returns a boolean value) and a list and then returns the list of elements that satisfy the predicate.

ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]  -- [5,6,4]  ghci> filter (==3) [1,2,3,4,5]  -- [3]  ghci> filter even [1..10]  -- [2,4,6,8,10]  ghci> let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]  -- [[1,2,3],[3,4,5],[2,2]]

takeWhile takes a predicate and a list and then goes from the beginning of the list and returns its elements while the predicate holds true. Once an element is found for which the predicate doesn’t hold, it stops.

ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))  -- 166650

Lambdas

Lambdas are basically anonymous functions that are used because we need some functions only once. Normally, we make a lambda with the sole purpose of passing it to a higher-order function. To make a lambda, we write a \ (because it kind of looks like the greek letter lambda if you squint hard enough) and then we write the parameters, separated by spaces. After that comes a -> and then the function body. We usually surround them by parentheses, because otherwise they extend all the way to the right.

ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]  -- [3,8,9,8,7]  ghci> zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]  -- [153.0,61.5,31.0,15.75,6.6]

Only folds and horses

A fold takes a binary function, a starting value (I like to call it the accumulator) and a list to fold up. The binary function itself takes two parameters. The binary function is called with the accumulator and the first (or last) element and produces a new accumulator. Then, the binary function is called again with the new accumulator and the now new first (or last) element, and so on. Once we’ve walked over the whole list, only the accumulator remains, which is what we’ve reduced the list to.

sum' :: (Num a) => [a] -> a  sum' xs = foldl (\acc x -> acc + x) 0 xs  -- The lambda function (\acc x -> acc + x) is the same as (+).ghci> sum' [3,5,2,1]  -- 11

foldr works in a similar way to the left fold, only the accumulator eats up the values from the right.

The foldl1 and foldr1 functions work much like foldl and foldr, only you don’t need to provide them with an explicit starting value. They assume the first (or last) element of the list to be the starting value and then start the fold with the element next to it.

scanl and scanr are like foldl and foldr, only they report all the intermediate accumulator states in the form of a list. There are also scanl1 and scanr1, which are analogous to foldl1 and foldr1.

When using a scanl, the final result will be in the last element of the resulting list while a scanr will place the result in the head.

ghci> scanl (+) 0 [3,5,2,1]  -- [0,3,8,10,11]  ghci> scanr (+) 0 [3,5,2,1]  -- [11,8,3,1,0]  ghci> scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]  -- [3,4,5,5,7,9,9,9]  ghci> scanl (flip (:)) [] [3,2,1]  -- [[],[3],[2,3],[1,2,3]]

Function application with $

($) :: (a -> b) -> a -> b  f $ x = f x

Whereas normal function application (putting a space between two things) has a really high precedence, the $ function has the lowest precedence. Function application with a space is left-associative (so f a b c is the same as ((f a) b) c)), function application with $ is right-associative.

Well, because $ is right-associative, f (g (z x)) is equal to f $ g $ z x.

And so, we can rewrite sum (filter (> 10) (map (\*2) [2..10])) as sum $ filter (> 10) $ map (\*2) [2..10].

Function composition

(.) :: (b -> c) -> (a -> b) -> a -> c  f . g = \x -> f (g x)

The expression negate . (* 3) returns a function that takes a number, multiplies it by 3 and then negates it.

Function composition is right-associative, so we can compose many functions at a time. The expression f (g (z x)) is equivalent to (f . g . z) x.

Many times, a point free style is more readable and concise, because it makes you think about functions and what kind of functions composing them results in instead of thinking about data and how it’s shuffled around.

Modules

Loading Modules

A Haskell module is a collection of related functions, types and typeclasses.

A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something.

Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file.

One script can, of course, import several modules. Just put each import statement into a separate line.

import Data.List (nub, sort)  import Data.List hiding (nub)  import qualified Data.Map  import qualified Data.Map as M

Data.List

intersperse

intersperse takes an element and a list and then puts that element in between each pair of elements in the list.

ghci> intersperse '.' "MONKEY"  -- "M.O.N.K.E.Y"  ghci> intersperse 0 [1,2,3,4,5,6]  -- [1,0,2,0,3,0,4,0,5,0,6]

intercalate

intercalate takes a list of lists and a list. It then inserts that list in between all those lists and then flattens the result.

ghci> intercalate " " ["hey","there","guys"]  -- "hey there guys"  ghci> intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]]  -- [1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]

transpose

transpose transposes a list of lists. If you look at a list of lists as a 2D matrix, the columns become the rows and vice versa.

ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]  -- [18,8,6,17]

concat

concat flattens a list of lists into just a list of elements.

ghci> concat ["foo","bar","car"]  -- "foobarcar"  ghci> concat [[3,4,5],[2,3,4],[2,1,1]]  -- [3,4,5,2,3,4,2,1,1]

It will just remove one level of nesting. So if you want to completely flatten [[[2,3],[3,4,5],[2]],[[2,3],[3,4]]], which is a list of lists of lists, you have to concatenate it twice.

concatMap

Doing concatMap is the same as first mapping a function to a list and then concatenating the list with concat.

ghci> concatMap (replicate 4) [1..3]  -- [1,1,1,1,2,2,2,2,3,3,3,3]

and

and takes a list of boolean values and returns True only if all the values in the list are True.

ghci> and $ map (>4) [5,6,7,8]  -- True  ghci> and $ map (==4) [4,4,4,3,4]  -- False

or

or is like and, only it returns True if any of the boolean values in a list is True.

ghci> or $ map (==4) [2,3,4,5,6,1]  -- True  ghci> or $ map (>4) [1,2,3]  -- False

any & all

any and all take a predicate and then check if any or all the elements in a list satisfy the predicate, respectively.

Usually we use these two functions instead of mapping over a list and then doing and or or.

ghci> any (==4) [2,3,5,6,1,4]  -- True  ghci> all (>4) [6,9,10]  -- True  ghci> all (`elem` ['A'..'Z']) "HEYGUYSwhatsup"  -- False  ghci> any (`elem` ['A'..'Z']) "HEYGUYSwhatsup"  -- True

iterate

iterate takes a function and a starting value. It applies the function to the starting value, then it applies that function to the result, then it applies the function to that result again, etc. It returns all the results in the form of an infinite list.

ghci> take 10 $ iterate (*2) 1  -- [1,2,4,8,16,32,64,128,256,512]  ghci> take 3 $ iterate (++ "haha") "haha"  -- ["haha","hahahaha","hahahahahaha"]

splitAt

splitAt takes a number and a list. It then splits the list at that many elements, returning the resulting two lists in a tuple.

ghci> splitAt 3 "heyman"  -- ("hey","man")  ghci> splitAt 100 "heyman"  -- ("heyman","")  ghci> splitAt (-3) "heyman"  -- ("","heyman")  ghci> let (a,b) = splitAt 3 "foobar" in b ++ a  -- "barfoo"

takeWhile

takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn’t satisfy the predicate, it’s cut off. It turns out this is very useful.

ghci> sum $ takeWhile (<10000) $ map (^3) [1..]  -- 53361

dropWhile

dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

ghci> dropWhile (/=' ') "This is a sentence"  -- " is a sentence"  ghci> dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1]  -- [3,4,5,4,3,2,1]

span

span is kind of like takeWhile, only it returns a pair of lists.

  • The first list contains everything the resulting list from takeWhile would contain if it were called with the same predicate and the same list.
  • The second list contains the part of the list that would have been dropped.
ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest  -- "First word: This, the rest: is a sentence"

break

Whereas span spans the list while the predicate is true, break breaks it when the predicate is first true.

Doing break p is the equivalent of doing span (not . p).

ghci> break (==4) [1,2,3,4,5,6,7]  -- ([1,2,3],[4,5,6,7])  ghci> span (/=4) [1,2,3,4,5,6,7]  -- ([1,2,3],[4,5,6,7])

When using break, the second list in the result will start with the first element that satisfies the predicate.

sort

sort simply sorts a list.

The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can’t be put in some kind of order, then the list can’t be sorted.

ghci> sort [8,5,3,2,1,6,4,2]  -- [1,2,2,3,4,5,6,8]  ghci> sort "This will be sorted soon"  -- "    Tbdeehiillnooorssstw"

group

group takes a list and groups adjacent elements into sublists if they are equal.

ghci> group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]  -- [[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]

If we sort a list before grouping it, we can find out how many times each element appears in the list.

ghci> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]  -- [(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]

inits & tails

inits and tails are like init and tail, only they recursively apply that to a list until there’s nothing left. Observe.

ghci> inits "w00t"  -- ["","w","w0","w00","w00t"]  ghci> tails "w00t"  -- ["w00t","00t","0t","t",""]  ghci> let w = "w00t" in zip (inits w) (tails w)  -- [("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]

isInfixOf & isPrefixOf & isSuffixOf

isInfixOf searches for a sublist within a list and returns True if the sublist we’re looking for is somewhere inside the target list.

ghci> "cat" `isInfixOf` "im a cat burglar"  -- True  ghci> "Cat" `isInfixOf` "im a cat burglar"  -- False  ghci> "cats" `isInfixOf` "im a cat burglar"  -- False

isPrefixOf and isSuffixOf search for a sublist at the beginning and at the end of a list, respectively.

ghci> "hey" `isPrefixOf` "hey there!"  -- True  ghci> "hey" `isPrefixOf` "oh hey there!"  -- False  ghci> "there!" `isSuffixOf` "oh hey there!"  -- True  ghci> "there!" `isSuffixOf` "oh hey there"  -- False

elem & notElem

elem and notElem check if an element is or isn’t inside a list.

partition

partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"  -- ("BOBMORGAN","sidneyeddy")  ghci> partition (>3) [1,3,5,6,3,2,1,0,3,7]  -- ([5,6,7],[1,3,3,2,1,0,3])

find

find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We’ll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int.

ghci> find (>4) [1,2,3,4,5,6]  -- Just 5  ghci> find (>9) [1,2,3,4,5,6]  -- Nothing  ghci> :t find  -- find :: (a -> Bool) -> [a] -> Maybe a

Data.Char

isControl checks whether a character is a control character.

isSpace checks whether a character is a white-space characters. That includes spaces, tab characters, newlines, etc.

isLower checks whether a character is lower-cased.

isUpper checks whether a character is upper-cased.

isAlpha checks whether a character is a letter.

isAlphaNum checks whether a character is a letter or a number.

isPrint checks whether a character is printable. Control characters, for instance, are not printable.

isDigit checks whether a character is a digit.

isOctDigit checks whether a character is an octal digit.

isHexDigit checks whether a character is a hex digit.

isLetter checks whether a character is a letter.

isMark checks for Unicode mark characters. Those are characters that combine with preceding letters to form latters with accents. Use this if you are French.

isNumber checks whether a character is numeric.

isPunctuation checks whether a character is punctuation.

isSymbol checks whether a character is a fancy mathematical or currency symbol.

isSeparator checks for Unicode spaces and separators.

isAscii checks whether a character falls into the first 128 characters of the Unicode character set.

isLatin1 checks whether a character falls into the first 256 characters of Unicode.

isAsciiUpper checks whether a character is ASCII and upper-case.

isAsciiLower checks whether a character is ASCII and lower-case.

toUpper converts a character to upper-case. Spaces, numbers, and the like remain unchanged.

toLower converts a character to lower-case.

toTitle converts a character to title-case. For most characters, title-case is the same as upper-case.

digitToInt converts a character to an Int. To succeed, the character must be in the ranges ‘0’…‘9’, ‘a’…‘f’ or ‘A’…‘F’.

intToDigit is the inverse function of digitToInt. It takes an Int in the range of 0…15 and converts it to a lower-case character.

The ord and chr functions convert characters to their corresponding numbers and vice versa:

ghci> ord 'a'  -- 97  ghci> chr 97  -- 'a'  ghci> map ord "abcdefgh"  -- [97,98,99,100,101,102,103,104]

Data.Map

Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn’t matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people’s names would be the keys. We don’t care in which order they’re stored, we just want to get the right phone number for the right person.

The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value.

fromList

The fromList function takes an association list (in the form of a list) and returns a map with the same associations.

ghci> Map.fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")]  -- fromList [("betty","555-2938"),("bonnie","452-2928"),("lucille","205-2928")]  ghci> Map.fromList [(1,2),(3,4),(3,2),(5,5)]  -- fromList [(1,2),(3,2),(5,5)]

empty

empty represents an empty map. It takes no arguments, it just returns an empty map.

ghci> Map.empty  -- fromList []

insert

insert takes a key, a value and a map and returns a new map that’s just like the old one, only with the key and value inserted.

ghci> Map.empty  -- fromList []  ghci> Map.insert 3 100 Map.empty  -- fromList [(3,100)]  ghci> Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100  Map.empty))  -- fromList [(3,100),(4,200),(5,600)]  ghci> Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty  -- fromList [(3,100),(4,200),(5,600)]

null

null checks if a map is empty.

size

size reports the size of a map.

singleton

singleton takes a key and a value and creates a map that has exactly one mapping.

lookup

lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn’t.

member

member is a predicate takes a key and a map and reports whether the key is in the map or not.

map & filter

map and filter work much like their list equivalents.

toList

toList is the inverse of fromList.

keys & elems

keys and elems return lists of keys and values respectively. keys is the equivalent of map fst . Map.toList and elems is the equivalent of map snd . Map.toList.

Data.Set

The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

import qualified Data.Set as Set

fromList

The fromList function works much like you would expect. It takes a list and converts it into a set.

intersection

As you can see, the items are ordered and each element is unique. Now let’s use the intersection function to see which elements they both share.

difference

We can use the difference function to see which letters are in the first set but aren’t in the second one and vice versa.

union

Or we can see all the unique letters used in both sentences by using union.

null & size & member & empty & singleton & insert & delete

The null, size, member, empty, singleton, insert and delete functions all work like you’d expect them to.

Making our own modules

http://learnyouahaskell.com/modules#loading-modules

-- Geometry       -  the name of folder-- Sphere           -  the name of source file --  volume, area  -  the functions that to be exportedmodule Geometry.Sphere  ( volume  , area  ) where    volume :: Float -> Float  volume radius = (4.0 / 3.0) * pi * (radius ^ 3)    area :: Float -> Float  area radius = 4 * pi * (radius ^ 2)

Making Our Own Types and Typeclasses

data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

data Bool = False | True
data Shape = Circle Float Float Float | Rectangle Float Float Float Float   data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)   surface :: Shape -> Float  surface (Circle _ _ r) = pi * r ^ 2  surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
data Point = Point Float Float deriving (Show)  data Shape = Circle Point Float | Rectangle Point Point deriving (Show)  surface :: Shape -> Float  surface (Circle _ r) = pi * r ^ 2  surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)  ghci> surface (Rectangle (Point 0 0) (Point 100 100))  -- 10000.0  ghci> surface (Circle (Point 0 0) 24)  -- 1809.5574
-- By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It's the same as writing Shape (Rectangle, Circle).module Shapes   ( Point(..)  , Shape(..)  , surface  , nudge  , baseCircle  , baseRect  ) where

Record syntax

data Person = Person { firstName :: String                       , lastName :: String                       , age :: Int                       , height :: Float                       , phoneNumber :: String                       , flavor :: String                       } deriving (Show)

Type parameters

data Maybe a = Nothing | Just a

The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

data Vector a = Vector a a a deriving (Show)    vplus :: (Num t) => Vector t -> Vector t -> Vector t  (Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n)    vectMult :: (Num t) => Vector t -> t -> Vector t  (Vector i j k) `vectMult` m = Vector (i*m) (j*m) (k*m)    scalarMult :: (Num t) => Vector t -> Vector t -> t  (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n

Derived instances

data Person = Person { firstName :: String                       , lastName :: String                       , age :: Int                       } deriving (Eq, Show, Read)
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday              deriving (Eq, Ord, Show, Read, Bounded, Enum)

Type synonyms

type String = [Char]

Input & Output

An I/O action will be performed when we give it a name of main and then run our program.

main = do      putStrLn "Hello, what's your name?"      name <- getLine      putStrLn ("Hey " ++ name ++ ", you rock!")

Because of that, main always has a type signature of main :: IO something, where *something* is some concrete type. By convention, we don’t usually specify a type declaration for main.

import Data.Char    main = do      putStrLn "What's your first name?"      firstName <- getLine      putStrLn "What's your last name?"      lastName <- getLine      let bigFirstName = map toUpper firstName          bigLastName = map toUpper lastName      putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?"

Files and Streams

getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String.

Functionally Solving Problems

Reverse Polish notation calculator

solveRPN :: (Num a, Read a) => String -> a  solveRPN = head . foldl foldingFunction [] . words      where foldingFunction (x:y:ys) "*" = (x * y):ys                 foldingFunction (x:y:ys) "+" = (x + y):ys                 foldingFunction (x:y:ys) "-" = (y - x):ys                 foldingFunction xs numberString = read numberString:xs
type FilePath = String  data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode

Haskell函数式编程学习笔记相关推荐

  1. Java 8 函数式编程学习笔记

    Java 8 函数式编程学习笔记 @(JAVASE)[java8, 函数式编程, lambda] Java 8 函数式编程学习笔记 参考内容 Java 8中重要的函数接口 扩展函数接口 常用的流操作 ...

  2. 【Java】函数式编程学习笔记——Stream流

    学习视频:https://www.bilibili.com/video/BV1Gh41187uR?p=1 (1)[Java]函数式编程学习笔记--Lambda表达式 (2)[Java]函数式编程学习笔 ...

  3. 专访《Haskell函数式编程入门》作者张淞:浅谈Haskell的优点与启发

    张淞,Haskell语言爱好者,著有<Haskell函数式编程入门>一书.目前就职于网易杭州研究院.在10月15日~17日的QCon上海2015上,他将分享<Haskell中的函数与 ...

  4. 《Haskell函数式编程入门》—— 第1章,第1.5节第一个Haskell程序HelloWorld!

    本节书摘来自异步社区<Haskell函数式编程入门>一书中的第1章,第1.5节第一个Haskell程序HelloWorld!,作者 张淞,更多章节内容可以访问云栖社区"异步社区& ...

  5. 《Haskell函数式编程入门》—— 第1章,第1.3节GHCi的使用

    本节书摘来自异步社区<Haskell函数式编程入门>一书中的第1章,第1.3节GHCi的使用,作者 张淞,更多章节内容可以访问云栖社区"异步社区"公众号查看 1.3 G ...

  6. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记--async和await(一) 接上文 多线程编程学习笔记--async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  7. 《Haskell函数式编程入门》——导读

    本节书摘来自异步社区<Haskell函数式编程入门>一书中的导读,作者 张淞,更多章节内容可以访问云栖社区"异步社区"公众号查看 第1章Haskell简介 第1章第1节 ...

  8. 多线程编程学习笔记——任务并行库(二)

    接上文 多线程编程学习笔记--任务并行库(一) 三.   组合任务 本示例是学习如何设置相互依赖的任务.我们学习如何创建一个任务的子任务,这个子任务必须在父任务执行结束之后,再执行. 1,示例代码如下 ...

  9. 多线程编程学习笔记——任务并行库(三)

    接上文 多线程编程学习笔记--任务并行库(一) 接上文 多线程编程学习笔记--任务并行库(二) 六.   实现取消选项 本示例学习如何实现基于Task的异步操作进行取消流程,以及在任务真正运行前如何知 ...

最新文章

  1. SQL 查询总是先执行SELECT语句吗?你们都错了!
  2. shell无上传cmd等exe文件权限解决办法
  3. 颠沛流离的Arcsight,辉煌不再
  4. [转]将图片转换为 latex 公式
  5. 涨姿势,图文带你了解 8 大排序算法
  6. javaone_代理的JavaOne 2016观察
  7. Android TabHost中实现标签的滚动以及一些TabHost开发的奇怪问题
  8. 解决Intellij IDEA运行报Command line is too long的问题
  9. 团队协助 开源项目_5分钟了解 Vtiger CRM-国际知名开源客户管理软件
  10. excel导出_学习笔记—— 前端导出excel
  11. ExtJs基础知识总结:自定义弹窗和ComboBox自动联想加载(四)
  12. 路飞学城python开发ftp_路飞学城-Python开发集训-第一章
  13. phpmyadmin mysql 5.1_phpMyAdmin(MySQL数据库管理)下载_phpMyAdmin(MySQL数据库管理) 版本:v5.1.0_魅蓝下载...
  14. 从小白到Python大神只需要100天
  15. 首款Unreal Engine 4引擎制作手机游戏曝光
  16. dell r510服务器怎么装系统,DellR510安装系统.docx
  17. qmake -v,出现错误:qmake: could not exec ‘/usr/lib/x86_64-linux-gnu/qt4/bin/qmake‘: No such file or direc
  18. 数商云智慧医疗管理系统解决方案:医药电商系统实现智能化改造
  19. 关心国事-周鸿祎离开雅虎真相 自称土鳖更喜欢创业
  20. 使用A*算法解迷宫最短路径问题

热门文章

  1. 单口RAM、双口RAM、FIFO
  2. 阅读软件汇-EPUB专版
  3. 高压直流电源系统(直流ups)有哪些特点?
  4. 编程实现输入两个正整数m和n,求其最大公约数和最小公倍数
  5. 用blender和MakeHuman生成人体动画
  6. 可恶的定理,但是却有大用
  7. Jenkins 构建CI/CD(一看就会)
  8. a5 1c语言实现,A5算法的C语言实现
  9. Zabbix5.0网易邮箱163告警详细步骤
  10. JavaScript时间换算单位