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 | 定义接口约束 |
typeof | typeof(x) | 获取表达式类型 |
is | T is int | 类型检查 |
练习
- 实现一个泛型链表
LinkedList[T] - 使用 concept 定义一个
Iterable约束 - 实现泛型
binarySearch[T]函数 - 创建一个泛型
Pair[A, B]类型,支持比较和排序
扩展阅读
← 上一章:面向对象编程 | 下一章:元编程 →