对于任何感兴趣的人,我想我会添加一个示例,说明截至 2019 年如何在Nim中解决此问题。
问题的第一部分是直截了当的,因为自从提出这个问题以来,Nim 已经获得了在浮点数(以及序数和枚举类型)上生成子范围类型的能力。下面的代码定义了两个新的浮点子范围类型,Probability
和ProbOne
.
问题的第二部分更加棘手——定义一个对其字段的函数有约束的类型。我提出的解决方案没有直接定义这种类型,而是使用宏 ( makePmf
) 将常量Table[T,Probability]
对象的创建与创建有效ProbOne
对象的能力联系起来(从而确保 PMF 有效)。宏在makePmf
编译时进行评估,确保您不能创建无效的 PMF 表。
请注意,我是 Nim 的新手,所以这可能不是编写此宏的最惯用的方法:
import macros, tables
type
Probability = range[0.0 .. 1.0]
ProbOne = range[1.0..1.0]
macro makePmf(name: untyped, tbl: untyped): untyped =
## Construct a Table[T, Probability] ensuring
## Sum(Probabilities) == 1.0
# helper templates
template asTable(tc: untyped): untyped =
tc.toTable
template asProb(f: float): untyped =
Probability(f)
# ensure that passed value is already is already
# a table constructor
tbl.expectKind nnkTableConstr
var
totprob: Probability = 0.0
fval: float
newtbl = newTree(nnkTableConstr)
# create Table[T, Probability]
for child in tbl:
child.expectKind nnkExprColonExpr
child[1].expectKind nnkFloatLit
fval = floatVal(child[1])
totprob += Probability(fval)
newtbl.add(newColonExpr(child[0], getAst(asProb(fval))))
# this serves as the check that probs sum to 1.0
discard ProbOne(totprob)
result = newStmtList(newConstStmt(name, getAst(asTable(newtbl))))
makePmf(uniformpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.25})
# this static block will show that the macro was evaluated at compile time
static:
echo uniformpmf
# the following invalid PMF won't compile
# makePmf(invalidpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.15})
注意:使用宏的一个很酷的好处是nimsuggest
(集成到 VS Code 中)甚至会突出显示创建无效 Pmf 表的尝试。