变量与常量 #

一、变量基础 #

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]

十、总结 #

变量与常量要点:

  1. 不可变性:Haskell变量一旦绑定就不能改变
  2. let表达式:表达式级别的绑定,作用域限于in后
  3. where子句:函数级别的绑定,作用域覆盖整个函数
  4. 常量:顶层绑定,整个模块可见
  5. 惰性求值:绑定只在需要时求值
  6. 模式绑定:可以在绑定中使用模式匹配

掌握变量与常量后,让我们继续学习类型签名。

最后更新:2026-03-27