px普贤教程PuXian
普贤教程 › 生态包

并发容器与数值

四个包,四种「容器与数值」的基础设施:concurrent_map 带锁映射(必须编译)、datastruct 堆/队列/LRU、dist 随机分布、fractions 精确有理数。它们的共同点是都有函数在当前版本里跑不通,逐个写明。

concurrent_map:必须编译,且两个函数是坏的

px
import concurrent_map

var m = cm_new()
cm_set(m, "a", 1)
cm_set(m, "b", 2)
print(cm_len(m), cm_get(m, "a"), cm_get(m, "z", 0))    # 2 1 0
print(str(cm_keys(m)), cm_has(m, "b"))                 # [a, b] true
cm_delete(m, "a")
print(cm_len(m), str(cm_keys(m)))                      # 1 [b]
text
2 1 0
[a, b] true
1 [b]
解释轨直接拒绝
px run 跑这个包会在 cm_new() 处终止:R1002: interp 不支持 mutex(Mini 子集排除)。 它是编译期专用:px build 出二进制再跑。用 px run 试包,会误判成「包坏了」。
cm_values 与 cm_to_string 恒崩
结构体实际键名是 "m"(cm_new() = {"m": {}, "mu": mutex()}),但这两个函数读的是 m["data"]:R1008: 字典没有键 'data'。取所有值请自己 for k in cm_keys(m): cm_get(m, k)。

datastruct:堆、队列、LRU

px
import datastruct

var h = ds_heap_new()
ds_heap_push(h, 3, "c")
ds_heap_push(h, 1, "a")
ds_heap_push(h, 2, "b")
print(ds_heap_peek(h), ds_heap_size(h))          # Ok(a) 3
print(ds_heap_pop(h), ds_heap_pop(h), ds_heap_pop(h))   # Ok(a) Ok(b) Ok(c)
text
Ok(a) 3
Ok(a) Ok(b) Ok(c)

最小堆,pop 返 Result;空堆取是 Err 不是 null。

px
var c = ds_lru_new(2)
ds_lru_set(c, "a", 1)
ds_lru_set(c, "b", 2)
ds_lru_get(c, "a")            # 触碰 a,b 变成最久未用
ds_lru_set(c, "c", 3)         # 容量 2 → 淘汰 b
print(ds_lru_has(c, "a"), ds_lru_has(c, "b"))   # true false
text
true false

另有 ds_queue_*(FIFO)、ds_stack_*(LIFO)、ds_deque_*,pop 一律返 Result、len 返 int。

dist:随机分布,两个函数用不了

px
import dist

print(di_uniform(1, 3))          # 1.806187200134066
print(di_randint(1, 6))          # 4
print(di_normal(0, 1))           # Ok(0.0035649755742717427)
print(di_bernoulli(0.5))         # Ok(0)
print(di_poisson(3))             # Ok(3)
print(di_bernoulli_mean(0.3), di_poisson_mean(3), di_poisson_var(3))   # 0.3 3 3
print(di_choice(["a","b","c"]).ok())    # a
两个函数恒崩,都在「应该很好用」的位置
① di_seed(42) → R1001: 未定义变量: 'srand'——包里调了不存在的内建,无法设种子,也就无法复现随机序列;
② di_normal_cdf(x, mean, sd) → R1001: 未定义变量: 'erf'——正态 CDF 依赖的 erf 语言里没有。
要可复现的随机数,用内建 random_int(n)(0..n-1)自己搭。

返回值形态:di_uniform / di_randint 直接给数值;di_normal / di_exponential / di_bernoulli / di_poisson / di_choice / di_shuffle 给 Result。同一个包里两种形态并存,用前先看一个 .is_ok() 试探。

fractions:精确有理数,但每步都要 .ok()

px
import fractions

var a = frac(1, 3).ok()
var b = frac(1, 6).ok()

print(frac_to_str(a), frac_to_str(b))              # 1/3 1/6
print(frac_to_str(frac_add(a, b).ok()))            # 1/2
print(frac_to_str(frac_mul(a, b).ok()))            # 1/18
print(frac_to_str(frac_pow(a, 2).ok()))            # 1/9
print(frac_to_float(a))                            # 0.3333333333333333
print(frac_to_str(frac(2, 4).ok()))                # 1/2(自动约分)
print(frac_to_str(frac_from_str("3/4").ok()))      # 3/4
print(frac(1, 0).is_err())                         # true
text
1/3 1/6
1/2
1/18
1/9
0.3333333333333333
1/2
3/4
true
契约不一致:构造器返 Result,运算器却按 list 取值
frac() 返 Ok([分子, 分母]),但 frac_add(a, b) 内部写的是 a[0] * b[1]—— 直接把 Result 传进去就是 R1002: 此类型不支持索引: result。每一步都要 .ok(), 包括 frac_to_str(frac(2,4)) 这种「只算一次」的写法。

能力面:frac_add/sub/mul/div/neg/cmp/eq/is_zero/pow/abs/reciprocal/floor/ceil/round/to_float/to_str、frac_gcd。范围是 64 位整数,超了会溢出(要更大用 big)。