d的隐藏类型

原文
首先,介绍一下背景.
D中的输入区间是带front,popFront,empty成员的类型:
它构成了迭代的基础.只是为了好玩,设计一个返回永久随机数序列输入区间.也叫生成器.
如下(不是很好的随机数生成器,但暂时可以):

module rnd;
struct RandomNumberGenerator {
    this(uint seed) {
        next = seed;
    popFront();   // 让他跑
    }
    @property int front() {
    return ((next / 0x10000) * next) >> 16;
    }
    void popFront() {
    next = next * 1103515245 + 12345;
    }
    @property bool empty() { return false; }
  private:
    uint next;
}

及返回它的函数:

RandomNumberGenerator generator(uint seed) {
    return RandomNumberGenerator(seed);
}

还有一个可爱的程序,可打印出10个此数字:

import std.stdio;
import rnd;
void main() {
  int count;
  foreach (n; generator(5)) {
      writeln(n);
      if (++count == 10)
          break;
  }
}

结果是:

26298
25347
52004
26314
22713
9193
9426
118
36355
10786

但是,它有些烦人.我真正关心的是rnd.generator函数,但是RandomNumberGenerator类型自身就在那里.它像是失败的封装,因为它在生成器抽象之外泄漏了.
可用属性标记它,rnd以外模块无法访问它.但它仍然在那,超出了它所属的空间,及其他模块成员仍然可访问它,私有与否(在D中,声明不会隐藏同一模块中的其他声明).此外,我希望它很干净,它会发出吱吱声.
现在谈谈有趣的.
首先,D允许推导声明类型,所以可写如下内容:

auto g = RandomNumberGenerator(seed);

g自动为随机数生成器,这是标准的.
接着,可推导出函数的返回类型:

auto square(double d) {
    return d * d;
}
auto x = square(2.3);

因为这是中语句式,编译器会把返回square的类型设置double.当然,也会推导x双精.现在重新组织生成器函数,如下:

module rnd;
auto generator(uint seed) {
    struct RandomNumberGenerator {
    @property int front() {
        return ((seed / 0x10000) * seed) >> 16;
    }
    void popFront() {
        seed = seed * 1103515245 + 12345;
    }
    @property bool empty() { return false; }
    }
    RandomNumberGenerator g;
    g.popFront();    // 让他跑
    return g;
 }

随机数生成器成为在生成器的域内的一个类型.它在生成器外根本不可见.不能命名它,因此它是隐藏类型.
只能通过推导类型来取它的实例:

auto g = generator(5);

然后使用g.使用typeof并声明另一个RandomNumberGenerator:

auto g = generator(4);
typeof(g) h;

抱歉,这不行,编译器禁止在区间实例化隐藏类型(原因是它无种子局部变量的引用).
最后细节很恼火.循环:

int count;
foreach (n; generator(5)) {
    writeln(n);
    if (++count == 10)
    break;
}

只是很老套.使用区间,可省略许多循环,取而代之的是使用区间来仅抓取区间的前10个元素:

void main() {
    foreach (n; take(generator(5), 10))
    writeln(n);
}

然后使用writeln来完全摆脱循环:

void main() {
    writeln(take(generator(5), 10));
}

给定T类型,及可从与T有定义关系的T中提取的U类型,那么U存在的.
如,如果看到T指针类型,可继承U指向的现有类型:

import std.stdio;
void extractU(T)(T t) {
    static if (is(T U : U*))
      writefln("%s类型是%s的指针", typeid(T), typeid(U));
    else
      writefln("%s类型不是%s的指针", typeid(T));
}
void main() {
    int* t;
    extractU(t);
    double d;
    extractU(d);
}

打印:

int*是int的指针
double不是.

结论

在D中,隐藏类型是个令人高兴的发现,并且按可用但不命名封装类型来启用令人满意的技术.

posted @   zjh6  阅读(23)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示