score API
包 Luna-Flow/mooncake-impact-factor/score 根据四个整数信号计算 MoonBit 包的影响力分数,为分数加上等级和势头类别标签,并把所有结果打包成一个 ScoreSnapshot。每个函数都是纯函数且是全函数:从不中止,也没有隐藏状态。
源码:src/score/impact_factor.mbt。公式背后的数学见 score 设计。
在 moon.pkg 中导入该包:
import {
"Luna-Flow/mooncake-impact-factor/score",
}
信号
每个评分函数都按以下顺序接收同样的四个信号:
| 参数 | 含义 |
|---|---|
dependents | 依赖本包的包的数量。 |
recent_dependents | 这些依赖方中首次出现在近期窗口内的数量。 |
downloads | 下载次数,未知时为 0。 |
days_since_release | 距最新发布的整天数。 |
这些函数不检查信号之间是否一致,例如是否满足 recent_dependents <= dependents。负值会被截为 0。索引构建器根据本地注册表快照填写这些信号;参见架构指南。
分数
compute_score
compute_score 返回一个包的影响力分数。
pub fn compute_score(Int, Int, Int, Int) -> Double
设 , 为依赖方数量, 为近期依赖方数量, 为下载量, 为距发布的天数,则结果为
其中 是 activity_multiplier(t)。当且仅当三个计数都为 或负数时分数为 ,并且任何计数增大时分数都不会减小。按设计分数没有上限,但当计数小于 时,分数始终低于约 。
test "compute_score" {
let score = @score.compute_score(20, 4, 300, 40)
inspect(score, content="301.7852882193072")
inspect(@score.compute_score(0, 0, 0, 10), content="0")
}
activity_multiplier
activity_multiplier 返回作用于整个分数的发布时效因子 。
pub fn activity_multiplier(Int) -> Double
负的 days_since_release 按 0 处理。
| 距发布的天数 | 乘数 |
|---|---|
0 到 30 | 1.12 |
31 到 90 | 1.06 |
91 到 180 | 1.0 |
181 到 365 | 0.94 |
366 或更多 | 0.88 |
test "activity_multiplier" {
inspect(@score.activity_multiplier(-3), content="1.12")
inspect(@score.activity_multiplier(90), content="1.06")
inspect(@score.activity_multiplier(400), content="0.88")
}
clamp_non_negative
clamp_non_negative 在 value 非负时返回 value,否则返回 0。
pub fn clamp_non_negative(Int) -> Int
评分函数会对每个信号应用它。它是公开的,以便调用方用同样的方式准备信号。
test "clamp_non_negative" {
inspect(@score.clamp_non_negative(-7), content="0")
inspect(@score.clamp_non_negative(12), content="12")
}
标签
rank_label
rank_label 把分数映射到等级桶 S、A、B、C、D 之一。
pub fn rank_label(Double) -> String
| 等级 | 条件 |
|---|---|
S | score >= 260.0 |
A | 180.0 <= score < 260.0 |
B | 110.0 <= score < 180.0 |
C | 50.0 <= score < 110.0 |
D | score < 50.0,或 score 为 NaN |
test "rank_label" {
inspect(@score.rank_label(260.0), content="S")
inspect(@score.rank_label(259.99), content="A")
inspect(@score.rank_label(12.0), content="D")
}
compute_momentum_label
compute_momentum_label 把分数增长的快慢分类为 Rising、Hot 或 Stable。
pub fn compute_momentum_label(Double, Double, Double, Int) -> String
参数依次为当前分数 、30 天前的分数 、增长率 和近期依赖方数量 。设增长量 :
| 标签 | 条件 |
|---|---|
Rising | 且 且 |
Hot | 不是 Rising,且 、、 |
Stable | 其他情况,包括任一参数为 NaN |
该函数不检查 growth_ratio 是否与两个分数相符;compute_score_snapshot 会替你计算它。
test "compute_momentum_label" {
inspect(@score.compute_momentum_label(140.0, 100.0, 0.4, 3), content="Rising")
inspect(@score.compute_momentum_label(140.0, 100.0, 0.4, 2), content="Hot")
inspect(@score.compute_momentum_label(105.0, 100.0, 0.05, 1), content="Stable")
}
快照
ScoreSnapshot
ScoreSnapshot 是对一个包在某一时间点评分的完整结果。
pub struct ScoreSnapshot {
score : Double
score_30d_ago : Double
score_growth_30d : Double
score_growth_ratio_30d : Double
rank_label : String
momentum_label : String
activity_multiplier : Double
} derive(ToJson, @debug.Debug, @json.FromJson)
| 字段 | 含义 |
|---|---|
score | 当前分数 。 |
score_30d_ago | 由历史信号计算出的分数 。 |
score_growth_30d | ;分数下降时为负。 |
score_growth_ratio_30d | 当 时为 ;否则 时为 , 时为 。 |
rank_label | rank_label(score). |
momentum_label | compute_momentum_label(score, score_30d_ago, score_growth_ratio_30d, recent_dependents). |
activity_multiplier | 当前信号对应的 activity_multiplier(days_since_release)。 |
这些字段在包外是只读的;请用 compute_score_snapshot 构造快照。ToJson 写出一个以字段名为键的对象,这正是 cli 命令的输出格式。FromJson 读回同样的格式,字段缺失或类型错误时抛出 @json.JsonDecodeError。Debug 支持 debug_inspect 和 Repr(...)。该结构体没有实现 Eq;请通过字段或 JSON 形式比较快照。
test "ScoreSnapshot JSON round trip" {
let snapshot = @score.compute_score_snapshot(8, 2, 120, 12, 0, 0, 0, 0)
let json = Json(snapshot)
let back : @score.ScoreSnapshot = @json.from_json(json)
assert_eq(Json(back), json)
inspect(back.momentum_label, content="Hot")
}
compute_score_snapshot
compute_score_snapshot 对一个包分别计算当前和 30 天前的分数,并在一次调用中得出增长量、两个标签和乘数。
pub fn compute_score_snapshot(Int, Int, Int, Int, Int, Int, Int, Int) -> ScoreSnapshot
前四个参数是当前信号,后四个是 30 天前的信号,二者都按信号表的顺序排列:
compute_score_snapshot(
dependents, recent_dependents, downloads, days_since_release,
historical_dependents, historical_recent_dependents,
historical_downloads, historical_days_since_release,
)
势头标签使用当前的 recent_dependents。30 天前尚不存在的包,其历史信号都为 0;此时它的 score_30d_ago 为 ,增长率为 。
test "compute_score_snapshot" {
let snapshot = @score.compute_score_snapshot(20, 4, 300, 40, 10, 2, 0, 10)
debug_inspect(
snapshot,
content=(
#|{
#| score: 301.7852882193072,
#| score_30d_ago: 135.2764584196223,
#| score_growth_30d: 166.5088297996849,
#| score_growth_ratio_30d: 1.2308780976744749,
#| rank_label: "S",
#| momentum_label: "Rising",
#| activity_multiplier: 1.06,
#|}
),
)
}
已弃用
ScoreSnapshot 仍保留三个方法,它们是早期 MoonBit 版本根据其派生 trait 隐式生成的。这些方法已从接口文件中隐藏,使用时会产生警告。
| 已弃用 | 替代方案 |
|---|---|
ScoreSnapshot::to_json(s) | Json(s) |
ScoreSnapshot::from_json(json, path) | @json.from_json(json) |
ScoreSnapshot::to_repr(s) | Repr(s)、debug_inspect 或 @debug.to_string |