强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

11 泛型编程

第 11 章:泛型编程

11.1 泛型过程

# 基本泛型过程
proc max[T](a, b: T): T =
  if a > b: a else: b

echo max(10, 20)         # 20(T = int)
echo max(3.14, 2.71)     # 3.14(T = float)
echo max("abc", "def")   # "def"(T = string)

# 多类型参数
proc pair[A, B](a: A, b: B): (A, B) =
  (a, b)

let p = pair(42, "hello")
echo p  # (42, "hello")

# 带约束的泛型
proc sum[T: SomeNumber](values: seq[T]): T =
  result = T(0)
  for v in values:
    result += v

echo sum(@[1, 2, 3, 4, 5])     # 15
echo sum(@[1.1, 2.2, 3.3])     # 6.6

11.2 泛型类型

# 泛型栈
type Stack[T] = object
  data: seq[T]

proc newStack[T](): Stack[T] =
  Stack[T](data: @[])

proc push[T](s: var Stack[T], item: T) =
  s.data.add(item)

proc pop[T](s: var Stack[T]): T =
  s.data.pop()

proc top[T](s: Stack[T]): T =
  s.data[^1]

proc isEmpty[T](s: Stack[T]): bool =
  s.data.len == 0

# 使用
var intStack = newStack[int]()
intStack.push(1)
intStack.push(2)
intStack.push(3)
echo intStack.pop()   # 3
echo intStack.top()    # 2

var strStack = newStack[string]()
strStack.push("hello")
strStack.push("world")
echo strStack.pop()    # "world"

# 泛型队列
type Queue[T] = object
  data: seq[T]

proc newQueue[T](): Queue[T] =
  Queue[T](data: @[])

proc enqueue[T](q: var Queue[T], item: T) =
  q.data.add(item)

proc dequeue[T](q: var Queue[T]): T =
  result = q.data[0]
  q.data.delete(0)

var q = newQueue[int]()
q.enqueue(10)
q.enqueue(20)
echo q.dequeue()  # 10
echo q.dequeue()  # 20

11.3 泛型对象与继承

type
  Container[T] = ref object of RootObj
    value: T

  NamedContainer[T] = ref object of Container[T]
    name: string

proc newContainer[T](value: T): Container[T] =
  Container[T](value: value)

proc newNamedContainer[T](name: string, value: T): NamedContainer[T] =
  NamedContainer[T](name: name, value: value)

proc `$`[T](c: Container[T]): string =
  "Container(" & $c.value & ")"

proc `$`[T](nc: NamedContainer[T]): string =
  nc.name & "=" & $nc.value

let c = newContainer(42)
let nc = newNamedContainer("config", 3.14)
echo c   # Container(42)
echo nc  # config=3.14

11.4 概念(Concepts)

概念(concepts)用于约束泛型类型必须满足的接口:

# 定义概念
type
  Addable = concept x, y
    x + y is type(x)

  Printable = concept x
    $x is string

  Container[T] = concept c
    c.len is int
    for item in c:
      item is T

# 使用概念约束
proc addValues[T: Addable](a, b: T): T =
  a + b

echo addValues(1, 2)       # 3
echo addValues(1.5, 2.5)   # 4.0
echo addValues("hi", " lo") # "hi lo"

proc printIt[T: Printable](x: T) =
  echo $x

printIt(42)
printIt(3.14)
printIt("hello")

proc sumAll[T: Container](c: T): auto =
  type Elem = typeof(c[0])
  result = default(Elem)
  for item in c:
    result += item

echo sumAll(@[1, 2, 3, 4])        # 10
echo sumAll(@[1.1, 2.2, 3.3])     # 6.6

11.5 常用约束类型

约束说明示例
SomeInteger所有整数类型int, int32, uint
SomeFloat所有浮点类型float, float32, float64
SomeNumber整数 + 浮点SomeInteger | SomeFloat
SomeSignedInt有符号整数int, int32, int64
SomeUnsignedInt无符号整数uint, uint32
Ordinal有序类型整数, 枚举, bool, char
Equatable可比较相等定义了 == 的类型
Sortable可排序定义了 < 的类型
# 整数专用
proc factorial[N: SomeInteger](n: N): N =
  result = N(1)
  for i in N(2)..n:
    result *= i

echo factorial(10)     # 3628800
echo factorial(20'u64) # 2432902008176640000

# 浮点专用
proc roundTo[T: SomeFloat](x: T, places: int): T =
  let factor = pow(10.0, places.float)
  round(x * factor) / factor

echo roundTo(3.14159, 2)  # 3.14

# 枚举约束
proc nextValue[T: Ordinal](x: T): T =
  succ(x)

type Color = enum Red, Green, Blue
echo nextValue(Red)   # Green
echo nextValue(Green) # Blue

11.6 is 和 typeof 操作符

# typeof 获取类型
let x = 42
type MyType = typeof(x)  # int
var y: MyType = 100

# 在泛型中使用 typeof
proc doubleSeq[T](s: seq[T]): seq[T] =
  result = newSeq[T](s.len * 2)
  for i, v in s:
    result[i * 2] = v
    result[i * 2 + 1] = v

echo doubleSeq(@[1, 2, 3])  # @[1, 1, 2, 2, 3, 3]

# is 检查类型
proc process[T](x: T) =
  when T is int:
    echo "Integer: ", x
  elif T is float:
    echo "Float: ", x
  elif T is string:
    echo "String: ", x
  else:
    echo "Other type"

process(42)       # Integer: 42
process(3.14)     # Float: 3.14
process("hello")  # String: hello

11.7 泛型与序列

import std/sequtils

# 泛型过滤
proc filterBy[T](data: seq[T], pred: proc(x: T): bool): seq[T] =
  result = @[]
  for item in data:
    if pred(item):
      result.add(item)

let nums = @[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evens = nums.filterBy(proc(x: int): bool = x mod 2 == 0)
echo evens  # @[2, 4, 6, 8, 10]

# 泛型映射
proc mapTo[T, U](data: seq[T], f: proc(x: T): U): seq[U] =
  result = newSeq[U](data.len)
  for i, v in data:
    result[i] = f(v)

let strs = nums.mapTo(proc(x: int): string = $x)
echo strs  # @["1", "2", "3", ...]

# 泛型聚合
proc reduce[T](data: seq[T], init: T, op: proc(a, b: T): T): T =
  result = init
  for v in data:
    result = op(result, v)

echo nums.reduce(0, proc(a, b: int): int = a + b)  # 55
echo nums.reduce(1, proc(a, b: int): int = a * b)  # 3628800

11.8 实战示例

🏢 场景:泛型缓存

import std/[tables, times, options]

type
  CacheEntry[T] = object
    value: T
    expiresAt: float

  Cache[K, V] = object
    entries: Table[K, CacheEntry[V]]
    defaultTTL: float  # 秒

proc newCache[K, V](defaultTTL: float = 60.0): Cache[K, V] =
  Cache[K, V](
    entries: initTable[K, CacheEntry[V]](),
    defaultTTL: defaultTTL
  )

proc set[K, V](cache: var Cache[K, V], key: K, value: V, ttl: float = 0) =
  let actualTTL = if ttl > 0: ttl else: cache.defaultTTL
  cache.entries[key] = CacheEntry[V](
    value: value,
    expiresAt: epochTime() + actualTTL
  )

proc get[K, V](cache: var Cache[K, V], key: K): Option[V] =
  if cache.entries.hasKey(key):
    let entry = cache.entries[key]
    if epochTime() < entry.expiresAt:
      return some(entry.value)
    else:
      cache.entries.del(key)
  return none(V)

# 使用
var c = newCache[string, int](defaultTTL = 5.0)
c.set("count", 42)
c.set("temp", 100, ttl = 2.0)

let val = c.get("count")
if val.isSome:
  echo "count = ", val.get()  # count = 42

🏢 场景:泛型结果类型

type
  ResultKind = enum Ok, Err
  Result[T, E] = object
    case kind: ResultKind
    of Ok:
      value: T
    of Err:
      error: E

proc ok[T, E](value: T): Result[T, E] =
  Result[T, E](kind: Ok, value: value)

proc err[T, E](error: E): Result[T, E] =
  Result[T, E](kind: Err, error: error)

proc isOk[T, E](r: Result[T, E]): bool = r.kind == Ok
proc isErr[T, E](r: Result[T, E]): bool = r.kind == Err

proc unwrap[T, E](r: Result[T, E]): T =
  if r.kind == Ok: r.value
  else: raise newException(CatchableError, "Unwrap on Err")

proc unwrapOr[T, E](r: Result[T, E], default: T): T =
  if r.kind == Ok: r.value
  else: default

# 使用示例
proc parseIntSafe(s: string): Result[int, string] =
  try:
    ok[int, string](parseInt(s))
  except ValueError:
    err[int, string]("Invalid number: " & s)

let r1 = parseIntSafe("42")
let r2 = parseIntSafe("abc")

echo r1.unwrap()           # 42
echo r2.unwrapOr(0)        # 0
echo r1.isOk()             # true
echo r2.isErr()            # true

本章小结

概念语法用途
泛型过程proc f[T](x: T)类型参数化函数
泛型类型type Foo[T]类型参数化类型
泛型约束[T: Constraint]限制类型范围
概念concept定义接口约束
typeoftypeof(x)获取表达式类型
isT is int类型检查

练习

  1. 实现一个泛型链表 LinkedList[T]
  2. 使用 concept 定义一个 Iterable 约束
  3. 实现泛型 binarySearch[T] 函数
  4. 创建一个泛型 Pair[A, B] 类型,支持比较和排序

扩展阅读


上一章:面向对象编程 | 下一章:元编程