ANU(Australian National University,澳大利亚国立大学)的 COMP1100 Programming as Communication 是 CS 专业最出名的"第一关"——它不用 Python 或 Java,而是用 Haskell,一种纯函数式编程语言。
对于绝大多数来自中国的留学生,Haskell 是一种从来没见过的编程思路。习惯了"命令式编程"(告诉计算机怎么一步一步做)的同学,需要切换到"声明式编程"(告诉计算机你要什么)。这个思维跳跃是 COMP1100 最大的难关。
为什么 ANU 用 Haskell 教编程?
ANU 的哲学是:让学生先学"正确"的编程方式——Haskell 的类型系统非常严格,能编译通过的程序通常逻辑上是正确的。学完 Haskell 之后,学 Python/Java 会更容易理解程序的本质。
Imperial College London 也用 Haskell 教 CO 120,所以这份指南对 Imperial 学生同样适用。
第一部分:Haskell 基础概念
1.1 函数式编程的核心思想
| 命令式(Python/Java) | 函数式(Haskell) |
|---|---|
| 用变量存储状态,逐步修改 | 没有可变变量,用函数变换数据 |
for / while 循环 | 递归代替循环 |
| 函数可能有副作用 | 纯函数,相同输入必有相同输出 |
| 类型可以隐式转换 | 强类型,类型错误编译时报错 |
1.2 基础语法
-- 定义一个函数:接受两个整数,返回它们的和
add :: Int -> Int -> Int
add x y = x + y
-- 使用
add 3 4 -- 结果:7
类型签名(Type Signature):
Int -> Int -> Int表示接受 Int,返回"接受 Int 的函数",最终返回 Int- 这体现了 Currying(柯里化):所有函数本质上都是单参数函数
1.3 基本数据类型
-- 基础类型
42 :: Int -- 整数
3.14 :: Double -- 浮点数
True :: Bool -- 布尔值
'a' :: Char -- 单字符
"hello" :: String -- 字符串(Char 的列表)
-- 列表(List)
[1, 2, 3] :: [Int]
['a', 'b', 'c'] :: [Char] -- 等同于 String "abc"
-- 元组(Tuple)
(1, True) :: (Int, Bool)
(1, 2, 3) :: (Int, Int, Int)
第二部分:Pattern Matching(模式匹配)
Pattern Matching 是 Haskell 最核心、也是 COMP1100 考试最多的内容。
2.1 基础 Pattern Matching
-- 对布尔值 Pattern Matching
not' :: Bool -> Bool
not' True = False
not' False = True
-- 对数字 Pattern Matching
factorial :: Int -> Int
factorial 0 = 1 -- 基础情形(Base Case)
factorial n = n * factorial (n - 1) -- 递归情形(Recursive Case)
执行顺序:从上到下匹配第一个符合的模式。
2.2 对列表 Pattern Matching
-- [] 匹配空列表
-- (x:xs) 匹配"第一个元素 x + 剩余列表 xs"
myLength :: [a] -> Int
myLength [] = 0 -- 空列表长度为 0
myLength (_:xs) = 1 + myLength xs -- 非空:1 + 剩余长度
myHead :: [a] -> a
myHead (x:_) = x
myHead [] = error "empty list"
mySum :: [Int] -> Int
mySum [] = 0
mySum (x:xs) = x + mySum xs
2.3 Guards(守卫条件)
classify :: Int -> String
classify n
| n < 0 = "negative"
| n == 0 = "zero"
| n < 10 = "small"
| otherwise = "large"
第三部分:高阶函数(Higher-Order Functions)
高阶函数是 Haskell 的精髓,COMP1100 期末必考。
3.1 map、filter、foldr
-- map:对列表每个元素应用函数
map :: (a -> b) -> [a] -> [b]
map (*2) [1,2,3] -- [2,4,6]
map even [1,2,3,4] -- [False,True,False,True]
-- filter:保留满足条件的元素
filter :: (a -> Bool) -> [a] -> [a]
filter even [1,2,3,4,5] -- [2,4]
filter (>3) [1,2,3,4,5] -- [4,5]
-- foldr:从右向左"折叠"列表(右折叠)
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr (+) 0 [1,2,3] -- 0+3+2+1 = 6(实际上是 1+(2+(3+0)))
foldr (:) [] [1,2,3] -- [1,2,3](复制列表)
3.2 Lambda 表达式(匿名函数)
-- 传统写法
double :: Int -> Int
double x = x * 2
-- Lambda 写法
double = \x -> x * 2
-- 在 map 中直接使用
map (\x -> x * x) [1,2,3,4] -- [1,4,9,16]
3.3 函数组合(Function Composition)
-- (.) 是函数组合运算符
(f . g) x = f (g x)
-- 例:先过滤偶数,再翻倍
doubleEvens :: [Int] -> [Int]
doubleEvens = map (*2) . filter even
-- 等价于:doubleEvens xs = map (*2) (filter even xs)
doubleEvens [1,2,3,4,5] -- [4,8]
第四部分:自定义类型(Custom Types)
4.1 代数数据类型(Algebraic Data Types)
-- 定义一个 Shape 类型
data Shape = Circle Double
| Rectangle Double Double
| Triangle Double Double Double
-- 计算面积(Pattern Matching on custom type)
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rectangle w h) = w * h
area (Triangle a b c) = let s = (a+b+c)/2
in sqrt (s*(s-a)*(s-b)*(s-c))
4.2 Maybe 类型(安全处理可能失败的情况)
-- Maybe a 要么是 Nothing(无值),要么是 Just a(有值)
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)
-- 使用
safeDiv 10 2 -- Just 5
safeDiv 10 0 -- Nothing
COMP1100 考试/作业常见错误
| 错误 | 原因 | 修复 |
|---|---|---|
| 无限递归(Stack Overflow) | 忘记写 Base Case | 确保有终止条件 |
| 类型错误(Type Error) | 函数签名与实现不符 | 检查类型签名,或先不写让 GHC 推断 |
| Pattern 未覆盖(Non-exhaustive) | 没有处理所有情况 | 加 otherwise 或 _ 通配符 |
show / read 混淆 | show 把值转为字符串,read 反向 | 明确数据流方向 |
备考策略
- 先理解类型系统:Haskell 的错误信息很长,但类型错误是最常见的,学会读类型签名
- 大量练习 Pattern Matching:这是 COMP1100 的核心,特别是列表的递归处理
- 在 GHCi 中即时测试:
ghci是 Haskell 的交互式 REPL,随时测试小函数 - 画递归调用树:帮助理解递归逻辑,特别是
foldr和foldl的执行顺序
相关资源:
