变量与常量 #
一、变量基础 #
1.1 变量的本质 #
在Haskell中,"变量"实际上是不可变的绑定:
haskell
-- 变量绑定
x = 5
-- x是不可变的
-- x = 6 -- 错误!不能重新赋值
1.2 变量命名 #
haskell
-- 合法变量名(小写开头)
name = "Haskell"
firstName = "John"
_count = 0
myFunction = "value"
-- 非法变量名
-- Name = "Haskell" -- 错误!大写开头
-- 1name = "value" -- 错误!数字开头
1.3 变量与函数 #
在Haskell中,变量和函数没有本质区别:
haskell
-- "变量"实际上是零参数函数
pi = 3.14159
-- 函数
double x = x * 2
-- 它们都是绑定
二、let表达式 #
2.1 基本语法 #
haskell
-- let绑定
result = let x = 5 in x * x -- 25
-- 多个绑定
result2 = let x = 5
y = 10
in x + y -- 15
2.2 let在列表推导式中 #
haskell
-- 在列表推导式中使用let
calcCircleAreas :: [Double] -> [Double]
calcCircleAreas rs = [area | r <- rs, let area = pi * r * r]
-- 带条件的let
results = [x * y | x <- [1..5], let y = x * 2, y < 10]
-- [2, 4, 6, 8]
2.3 let在do块中 #
haskell
main :: IO ()
main = do
let name = "Haskell"
let year = 1990
putStrLn (name ++ " was created in " ++ show year)
2.4 let的作用域 #
haskell
-- let绑定的作用域限于in后的表达式
example = let x = 5 in x + y -- 错误!y不在作用域内
-- 嵌套let
nested = let x = 5
in let y = x * 2
in x + y -- 15
三、where子句 #
3.1 基本语法 #
haskell
-- where子句
area :: Double -> Double
area r = pi * r * r
where pi = 3.14159
-- 多个定义
volume :: Double -> Double -> Double -> Double
volume width height depth = width * height * depth
where
width = 10
height = 5
depth = 3
3.2 where与模式匹配 #
haskell
-- where中使用模式匹配
describe :: (Int, Int) -> String
describe point = "Point at (" ++ show x ++ ", " ++ show y ++ ")"
where (x, y) = point
3.3 where的作用域 #
haskell
-- where绑定在整个函数体内有效
calculate :: Int -> Int
calculate x = result + bonus
where
result = x * 2
bonus = if x > 10 then 100 else 0
3.4 嵌套where #
haskell
complexCalc :: Int -> Int
complexCalc x = outerResult
where
outerResult = innerResult + base
innerResult = x * multiplier
multiplier = 2
base = 10
四、let vs where #
4.1 语法位置 #
haskell
-- let是表达式,可以放在任何地方
letExample = (let x = 5 in x * x) + 10
-- where是声明,只能跟在函数定义后
whereExample = result + 10
where result = 5 * 5
4.2 作用域差异 #
haskell
-- let作用域限于in后的表达式
letScope = let x = 5 in x + y -- y不在作用域
-- where作用域覆盖整个函数体
whereScope = x + y
where
x = 5
y = 10
4.3 使用场景 #
haskell
-- let适合中间计算
quickCalc = let x = 5
y = 10
in x + y
-- where适合辅助函数
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = smaller ++ [x] ++ larger
where
smaller = [a | a <- xs, a <= x]
larger = [b | b <- xs, b > x]
五、常量 #
5.1 顶层绑定 #
haskell
-- 顶层常量
pi :: Double
pi = 3.141592653589793
e :: Double
e = 2.718281828459045
-- 常量在整个模块可见
circleArea :: Double -> Double
circleArea r = pi * r * r
5.2 命名约定 #
haskell
-- 常量通常使用小写驼峰
maxConnections = 100
defaultTimeout = 30
-- 有时使用全大写(类似其他语言)
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
5.3 常量表达式 #
haskell
-- 常量在编译时求值
maxInt :: Int
maxInt = maxBound :: Int
-- 表达式常量
goldenRatio :: Double
goldenRatio = (1 + sqrt 5) / 2
六、模式绑定 #
6.1 基本模式绑定 #
haskell
-- 元组模式绑定
point = (1, 2)
(x, y) = point
-- 列表模式绑定
list = [1, 2, 3]
(a:b:_) = list -- a = 1, b = 2
6.2 在where中使用 #
haskell
-- where中的模式绑定
getFirstTwo :: [a] -> (a, a)
getFirstTwo xs = (first, second)
where
(first:second:_) = xs
6.3 在let中使用 #
haskell
-- let中的模式绑定
extractFirst :: (a, b) -> a
extractFirst tuple = let (x, _) = tuple in x
七、惰性绑定 #
7.1 惰性求值 #
haskell
-- 未使用的绑定不会被求值
lazyExample = let expensive = sum [1..1000000] -- 不会计算
in 42
-- 只有需要时才求值
semiLazy = let x = 1 + 2
y = 3 + 4
in x -- y不会被求值
7.2 无限数据结构 #
haskell
-- 无限列表
ones :: [Int]
ones = 1 : ones
-- 使用惰性绑定
take 5 ones -- [1, 1, 1, 1, 1]
-- 无限斐波那契
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
八、实践示例 #
8.1 计算几何 #
haskell
-- 使用where组织辅助函数
triangleArea :: Double -> Double -> Double -> Double
triangleArea a b c = sqrt (s * (s - a) * (s - b) * (s - c))
where
s = (a + b + c) / 2
-- 使用let进行中间计算
quadraticRoots :: Double -> Double -> Double -> (Double, Double)
quadraticRoots a b c =
let discriminant = b * b - 4 * a * c
sqrtD = sqrt discriminant
twoA = 2 * a
in ((-b + sqrtD) / twoA, (-b - sqrtD) / twoA)
8.2 字符串处理 #
haskell
-- 使用let处理字符串
processName :: String -> String
processName fullName =
let parts = words fullName
firstName = head parts
lastName = last parts
in lastName ++ ", " ++ firstName
-- 使用where定义辅助函数
countWords :: String -> Int
countWords text = length (words text)
where
words s = case dropWhile (== ' ') s of
"" -> []
s' -> let word = takeWhile (/= ' ') s'
rest = dropWhile (== ' ') (drop (length word) s')
in word : words rest
8.3 数学计算 #
haskell
-- 阶乘
factorial :: Integer -> Integer
factorial n
| n <= 0 = 1
| otherwise = n * factorial (n - 1)
-- 斐波那契
fib :: Integer -> Integer
fib n
| n <= 0 = 0
| n == 1 = 1
| otherwise = fib (n - 1) + fib (n - 2)
-- 优化版本
fibFast :: Integer -> Integer
fibFast n = fibs !! fromIntegral n
where
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
九、最佳实践 #
9.1 选择let还是where #
haskell
-- 使用where:当辅助定义较多时
complexFunction x y = result
where
step1 = x * 2
step2 = y + 10
step3 = step1 + step2
result = step3 * 2
-- 使用let:当只需要临时计算时
simpleCalc x = let doubled = x * 2 in doubled + 1
9.2 命名建议 #
haskell
-- 使用有意义的名称
-- 好
area = pi * radius * radius
where pi = 3.14159
radius = 5
-- 不好
a = b * c * c
where b = 3.14159
c = 5
9.3 避免重复计算 #
haskell
-- 不好:重复计算
badExample x = expensive x + expensive x
where expensive n = sum [1..n]
-- 好:使用绑定
goodExample x = result + result
where
result = expensive x
expensive n = sum [1..n]
十、总结 #
变量与常量要点:
- 不可变性:Haskell变量一旦绑定就不能改变
- let表达式:表达式级别的绑定,作用域限于in后
- where子句:函数级别的绑定,作用域覆盖整个函数
- 常量:顶层绑定,整个模块可见
- 惰性求值:绑定只在需要时求值
- 模式绑定:可以在绑定中使用模式匹配
掌握变量与常量后,让我们继续学习类型签名。
最后更新:2026-03-27