Haskell 简易入门教程

Haskell 是面向实用而设计的纯函数式编程语言。 Haskell 以其单子系统与类型系统著称,不过我在这里介绍 Haskell 更是因为它的优雅。对我而言,使用 Haskell 写代码是一个非常愉快的过程。

在 Ubuntu 中安装 Haskell 环境非常简单: sudo apt-get install ghc ,然后键入 ghci 就进入了交互式命令行界面。

-- 单行注释由两个横杠(“-”)开头
{- 多行注释可以由
这样格式的花括号包裹
-}

----------------------------------------------------
-- 1. 基本数据类型与运算操作
----------------------------------------------------

-- 数字
3 -- 3

-- 数学运算正如你所熟悉的那样
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
35 / 5 -- 7.0

-- 除法运算不再是默认为整数除法
35 / 4 -- 8.75

-- 整数除法( div )
35 `div` 4 -- 8

-- 布尔类型是基本数据类型
True
False

-- 布尔操作
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True

-- 在上面的例子中, `not` 是一个接收一个参数的函数。
-- Haskell 中不需要使用圆括号包裹调用函数的参数。
-- 只需要简单的列到函数后面。一般的形式是这样的:
-- func arg1 arg2 arg3...
-- 具体可以阅览本文函数部分获取如何书写函数的信息。

-- 字符串与字符
"This is a string." -- 字符串
'a' -- 字符
'You cant use single quotes for strings.' -- 这么写是错误的!

-- 字符串合并
"Hello " ++ "world!" -- "Hello world!"

-- 字符串就是由字符组成的列表
"This is a string" !! 0 -- 'T' !! 是由列表索引取值的操作。


----------------------------------------------------
-- 列表与元组
----------------------------------------------------

-- 在一个列表中的所有元素必须是同一种类型
-- 以下两个列表是一样的。
[1, 2, 3, 4, 5]
[1..5]

-- 你也可以创建一个含有无限个元素的列表
[1..] -- 包含所有自然数的列表

-- 之所以 Haskell 中可以存在无限元素的列表,是因为 Haskell 有“惰性求值”的特性。这就是说
-- 在 Haskell 中只有当需要时才会去计算其值。所以你可以获取这个列表的第 1000 个元素:

[1..] !! 999 -- 1000

-- 现在,Haskell 会计算出这个列表的前 1000 项,但是这个无限列表余下的项仍然是不存在的,
-- 只有当实际需要的时候,Haskell 才会去计算获取其值。

- 合并两个列表
[1..5] ++ [6..10]

-- 将一个元素加入列表头
0:[1..5] -- [0, 1, 2, 3, 4, 5]

-- 对一个列表按索引取值
[0..] !! 5 -- 5

-- 其他列表操作
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5

-- 列表推导
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]

-- 带条件的列表推导
[x*2 | x <- [1..5], x > 4] -- [6, 8, 10]

-- 同一个元组中的元素可以是不同类型,但是元组有固定的长度。
-- 元组:
("haskell", 1)

-- 元组操作
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1

----------------------------------------------------
-- 3. 函数
----------------------------------------------------
-- 有两个参数的简单函数:
add a b = a + b

-- 需要注意的是,如果你是用的是 ghci
-- 那么,你需要是用 let ,例如:
-- let add a b = a + b

-- 是用函数
add 1 2 -- 3

-- 你也可以将函数名放到两个参数之间,需要是用反引号包裹:
1 `add` 2 -- 3

-- 你也可以自己定义函数名不包含字符的函数,这样你就可以定义自己的操作符了。
-- 这里定义了一个整数除法的操作符:
(//) a b = a `div` b
35 // 4 -- 8

-- 哨兵: 一种实现分支逻辑的简单方式
fib x
  | x < 2 = x
  | otherwise = fib (x - 1) + fib (x - 2)


-- 模式匹配与其类似。这里我们有三个不同的对于 fib 的定义。
-- Haskell 将会自动调用一个与值模式匹配的函数。
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)

-- 使用元组的模式匹配:
foo (x, y) = (x + 1, y + 2)

-- 对于列表的模式匹配
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)

-- 匿名函数使用反斜杠开头后跟所有的参数
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]

-- using fold (called `inject` in some languages) with an anonymous
-- function. foldl1 means fold left, and use the first value in the
-- array as the initial value for the accumulator.
foldl1 (\acc x -> acc + x) [1..5] -- 15

----------------------------------------------------
-- 4. 其他函数
----------------------------------------------------

-- 科里化


add a b = a + b
foo = add 10 -- 先给一个参数
foo 5 -- 15

-- 另一种写法
foo = (+10)
foo 5 -- 15

-- 函数组合
-- 使用 (.) 将函数链接起来
foo = (*5) . (+10)

-- (5 + 10) * 5 = 75
foo 5 -- 75

-- 固定优先级
-- $ 有更高的优先级

-- 本来
(even (fib 7)) -- true

-- 然后
even . fib $ 7 -- true

----------------------------------------------------
-- 5. 类型签名
----------------------------------------------------

-- 基本类型
5 :: Integer
"hello" :: String
True :: Bool

-- 函数也有类型
-- `not` takes a boolean and returns a boolean:
-- not :: Bool -> Bool

-- 两个参数的函数
-- add :: Integer -> Integer -> Integer

-- 定义函数式也定义类型是一个好习惯
double :: Integer -> Integer
double x = x * 2

----------------------------------------------------
-- 6. 流程控制
----------------------------------------------------

-- if 语句
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"

-- if statements can be on multiple lines too, indentation is important
haskell = if 1 == 1
            then "awesome"
            else "awful"

-- case statements: Here's how you could parse command line arguments
case args of
  "help" -> printHelp
  "start" -> startProgram
  _ -> putStrLn "bad args"

-- Haskell 中没有循环,取而代之的是递归

map (*2) [1..5] -- [2, 4, 6, 8, 10]

-- you can make a for function using map
for array func = map func array

-- and then use it
for [0..5] $ \i -> show i

-- we could've written that like this too:
for [0..5] show

----------------------------------------------------
-- 7. Data Types
----------------------------------------------------

-- Here's how you make your own data type in Haskell

data Color = Red | Blue | Green

-- Now you can use it in a function:

say :: Color -> IO String
say Red = putStrLn "You are Red!"
say Blue = putStrLn "You are Blue!"
say Green = putStrLn "You are Green!"

-- Your data types can have parameters too:

data Maybe a = Nothing | Just a

-- These are all of type Maybe
Nothing
Just "hello"
Just 1

----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------

-- 不牵扯 monads 是说不清楚 IO 的。

-- An `IO a` value is an IO action: you can chain them with do blocks
action :: IO String
action = do
   putStrLn "This is a line. Duh"
   input <- getLine -- this gets a line and gives it the name "input"
   input2 <- getLine
   return (input1 ++ "\n" ++ input2) -- This is the result of the whole action

-- This didn't actually do anything. When a haskell program is executed
-- an IO action called "main" is read and interpreted.

main = do
    putStrLn "Our first program. How exciting!"
    result :t foo
foo :: Integer

快排实现:

qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
    where lesser  = filter (<p>= p) xs

原文: http://learnxinyminutes.com/docs/haskell/