Skip to content

Instantly share code, notes, and snippets.

@Akagi201
Last active August 4, 2018 08:39
Show Gist options
  • Save Akagi201/81d18cf41964796a00045bca09e453ce to your computer and use it in GitHub Desktop.
Save Akagi201/81d18cf41964796a00045bca09e453ce to your computer and use it in GitHub Desktop.
[haskell]

transport

hackage

REPL

haddock

vscode

stack project

awesome haskell

hackage

REPL

haddock

安装

  • haskell-platform

编译与运行

# 交互式解释器
ghci
# 编译生成二进制可执行文件
ghc --make test.hs
# 运行 `test.hs`(不需要编译)
runghc test.hs
# `runghc` 的别名
runhaskell test.hs

注释

-- 单行注释
{- 
被 `{-` 和 `-}` 括起来的是多行注释
-}

数学运算

2 + 10      -- 12
2 - 10      -- -8
2 * 10      -- 20
2 / 10      -- 0.2
2 `div` 10  -- 0
2 `mod` 10  -- 2
2 ^ 10      -- 1024

布尔运算

注意: 只有布尔值能进行布尔运算, 所以类似 0 || 1 的表达式会报错.

not True         -- 非
False && True    -- 与
False || True    -- 或

12345 /= 54321   -- 不等于
"foo" == "foo"   -- True       
"abc" <= "bbb"   -- True
(2,0) >= (1,9)   -- True

列表

-- 列表中元素的类型相同
[1, 2, 3]
-- 将 1 添加到列表 [2, 3] 的头部,时间复杂度为 O(1)
1:[2, 3]
-- 从 1 到 3 的列表,实际上是 `1:2:3:[]` 的语法糖
[1..3]
-- 小写字母。字符串实际上是字符列表,即 `[Char]`
['a'..'z']          -- "abcdefghijklmnopqrstuvwxyz"
-- 根据头两个数生成列表
[1, 4..10]          -- [1,4,7,10]
[1, 4..11]          -- [1,4,7,10]
-- 无穷列表
[1..]               -- 1 到正无穷
[1, 0..]            -- 1 到负无穷

-- 比较两个列表。挨个元素比较大小,直到确定大小关系,时间复杂度为 O(n)
[3, 2..] > [1, 2..]
-- 合并两个列表。挨个将第一个列表的元素添加到第二个列表中,时间复杂度为 O(n)
[1, 3..9] ++ [2, 4..10]
"hello" ++ "world"
-- `list !! i` 表示取出列表 `list` 中下标为 `i` 的元素,下标从 0 开始
[0..] !! 999

-- `[2*x | x <- l]` 产生了一个新列表,它的元素由列表 `l` 中的元素进行运算 `2*x` 得到
[2*x | x <- [1..2]]                                         -- [2,4]
-- 对偶数计算 `2*x`
[2*x | x <- [1..9], x `mod` 2 == 0]                         -- [4,8,12,16]
-- 计算与原点的距离
[sqrt (x*x + y*y) | (x, y) <- [(1, 1), (5, 12), (3, 4)]]    -- [1.4142135623730951,13.0,5.0]
-- 计算笛卡尔积
[(x, y) | x <- [1, 2], y <- [3, 4]]                         -- [(1,3),(1,4),(2,3),(2,4)]
-- 筛选
[y | (3, y) <- [(1, 1), (5, 12), (3, 4)]]                   -- [4]

if 表达式

与其他语言的 if 不同, Haskell 的 if 是表达式, 有返回值, 所以, 必须有 else. 下面是几个例子:

if 1 > 0 then "good" else "WTF?!"
[if 0 <= x && x <= 9 then x else -1 | x <- [-3..12]]

let 语句

let 将表达式或值绑定到变量

let c = 3
c == let a = 1; b = 2 in a + b

let 有 let ...let ... in ... 两种形式。前者只能出现在 do 或列表解析中 | 的后面, 后者在任何表达式能够存在的地方都可以出现.

-- 出现在 `do` 中
do statements
   let var1 = expr1
       var2 = expr2
   statements

-- 出现在列表解析中
[(x, y) | x <- [1..2], let y = 2*x]                 -- [(1,2),(2,4)]

-- 作为表达式
(let a = 1; b = 2 in a + b) + 3                     -- 6
[(x, y) | x <- [1..2], let y = let a = x^2 in a+x]  -- [(1,2),(2,6)]

函数

语法形式:

name arg1 arg2 = do_something_with_args

Haskell 的函数有几个特点:

  • 函数参数之间用空格隔开
  • 任何函数都有返回值
  • 函数名不一定要是字母
let (+) = (++)
let (%) = mod
let pp' = succ

pp' 2                   -- 3
1 % 2                   -- 1
"hello" + "world"       -- "helloworld"

匿名函数

匿名函数是没有名字的函数, 它符合λ演算

\arg1 arg2 -> do_something_with_args

也许因为 \ 看起来像 λ, 所以被用来定义匿名函数吧.

(\x y -> x + y) 3 5                                           -- 8
[is_odd x | x <- [1..5], let is_odd = \x -> x `mod` 2 == 1]   -- [True,False,True,False,True]
foldl (\acc x -> 2*x + acc) 0 [1..3]   

常用函数

-- 类似于 `i++`
succ 2                          -- 3

-- 取出元组第一个元素
fst ("hello", "world")          -- "hello"
snd ("hello", "world")          -- "world"

head [1..5]                     -- 1
tail [1..5]                     -- [2,3,4,5]
last [1..5]                     -- 5
-- 丢弃最后一个元素形成的列表
init [1..5]                     -- [1,2,3,4]

length [1..5]                   -- 5
-- 检查列表是否为空
null []                         -- True
-- 检查列表中是否存在元素 9
elem 9 [1..5]                   -- False

-- 反转列表
reverse [1..5]                  -- [5,4,3,2,1]
-- 取出列表的前 5 个元素组成新列表
take 5 [1..]                    -- [1,2,3,4,5]
-- 丢弃列表的前 5 个元素组成新列表
drop 5 [1..9]                   -- [6,7,8,9]
-- 生成具有 5 个重复元素 'a' 的新列表
replicate 5 'a'                 -- "aaaaa"

sum [1..5]                      -- 15
-- 1*2*3*4*5
product [1..5]                  -- 120
maximum [1..5]                  -- 5
minimum [1..5]                  -- 1

-- `foldl f acc list` 相当于 `foreach x in list do acc = f(acc, x)`
foldl (+) 0 [1..5]              -- sum [1..5]
foldl max 0 [1..5]              -- maximum [1..5]
-- `foldr` 与 `foldl` 类似,只不过是从右到左遍历列表
foldr (-) 0 [1..5]              -- 3
foldl (-) 0 [1..5]              -- -15
-- `map f list` 相当于 `[f(x) | x <- list]`
map succ [1..5]                 -- [succ x | x <- [1..5]]
-- 过滤掉不满足 `x > 0` 的元素
filter (> 0) [-5..5]            -- [1,2,3,4,5]
-- `zip a b` 将列表 `a`、`b` 合并成新列表 `c`,其中 `c !! i == (a !! i, b !! i)`
zip [1, 3..9] [2, 4]            -- [(1,2),(3,4)]
-- `zipWith f a b` 将列表 `a`、`b` 合并成新列表 `c`,其中 `c !! i == f (a !! i) (b !! i)`
zipWith (+) [1, 3..9] [2, 4]    -- [3,7]

不同实现

haskell platform

Cabal

Stackage

tutorial

stack

learn you a haskell

vscode

为了保证版本最新,不使用包管理器安装。

其实用 stack 安装也可以,但主要是 stack setup 时下载速度太慢,以为是国内网速问题,然而配置了 tuna 的镜像后依然极慢无比,而且出现神奇的下载进度变少的情况(刚刚是2.x%,过一会儿变成了1.6% 😆 )

于是采用如下流程安装

  • 先把 haskell-platform 安装好(下载下来解压跑安装脚本),现在 ghc 和 cabal 都是最新的,不过 stack --version 发现 stack 版本比较老

  • 于是下载 stack 后解压放在 /usr/local/haskell/ 下,然后 rm -rf /usr/local/bin/stack 删除老的 stack 链接,再 ln -s /usr/local/haskell/stack-1.3.2-linux-x86_64-static/stack /usr/local/bin/stack 将新版本链接进去

  • 软件装好,允许 stack new 时发现更新包信息非常慢,而且看不到进度,于是将 stack 和 cabal 的仓库地质改成 tuna 提供的镜像,具体使用方式在这里

  • 安装完成,可以按照 stack 的新手指导来学习使用了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment