d模板元编程笔记04

1003 篇文章 1 订阅
48 篇文章 0 订阅
导入 标.类型元组;

元 静映射(别名 M, T...)
{
    静 如 (T.长度 == 0) // 序列尾 
        别名 元组类型!() 静映射; // 停止 
    异
        别名 元组类型!(M!(T[0]), 静映射!(M, T[1..$])) 静映射;
}
模块 有资格;
导入 标.类型元组;

元 合格(T)//给定类型,生成他们的合格类型
{
    别名 元组类型!(
         T,(T), 不变(T), 共享(T),
         T[],(T)[],不变(T)[], 共享(T)[],(T[]), 不变(T[]), 共享(T[])
        ) 合格;
}

// 16个内置类型
别名 元组类型!(, 正字节,字节, 正短,,,, 正长,,,双精,,,宽符,d符) 值类型;

// 注意, 生成11*16 类型 .
别名 静映射!(合格,值类型) 有资格类型;

// 有重复的.
别名 静映射!(合格, 有资格类型) 双资格类型;
模块 测试员;
导入 有资格;

空 我的函数(T)(T t) {}

元 测试(别名 函数)
{on(T...)()
    {
        每一(类型; T)
            静 如 (!__特征(编译, 函数(类型.初化)))
                指示(消息, "坏测试组合: "~ 函数.的串 ~ " 和 " ~ 类型.的串);
    }
}

单元测试
{
    测试!(我的函数).on!(有资格类型);
}//这里测试.

Unqual消除限定符.用类型元组!(...)来生成大量类型.给定一堆函数类型,生成返回类型/函数参数类型.假定,你有个对所有内置类型管用的模板函数,直接用静映射来生成.如果你函数应对所有生成类型起作用.则只需测试.
过滤类型.c++也有.判词,返回真/假的函数.

元 静过滤(别名 判断, T...)
{
    静 如 (T.长度 == 0) // 序列尾
        别名 元组类型!() 静过滤;
    异 静 如 (判断!(T[0]))
        别名 元组类型!(T[0],静过滤!(判断, T[1..$])) 静过滤;
    异
        别名 元组类型!(静过滤!(判断, T[1..$])) 静过滤;
}
模块 用静过滤1;
导入 标.类型元组;
导入 标.特征;
导入 静过滤;

别名 元组类型!(, 双精,,, 字节,,,) 类型;

别名 静过滤!(是整数, 类型) 仅整数;
静 断定((仅整数 == 元组类型!(, 字节)));
//
模块 是类型;

元 是类型(T)
{
    枚 是类型 =;
}

元 是类型(别名 a)
{
    静 如 ((a))枚 是类型 =;
    异 枚 是类型 =;
}//分开类型.
模块 是内置类型;

元 是符号(别名 a)
{
    枚 是符号 =;
}

元 是内置类型(T)
{
    静 如 (__特征(编译, 是符号!(T)))
        枚 是内置类型 =;
    异
        枚 是内置类型 =;
}

元 是内置类型(别名 a)
{
    枚 是内置类型 =;
}
模块 用静过滤2;
导入 标.类型元组;
导入 静过滤;
导入 是类型;
导入 是内置类型;

类 我的类 {}
整 福(整 i) { 中 i;}

别名 静过滤!(是类型, 1,, 3.14, "abc",, 我的类) 类型;
别名 静过滤!(是内置类型, 1,, 3.14, "abc",, 我的类) 内置;

静 断定((类型 == 元组类型!(, 我的类)));
静 断定((内置 == 元组类型!()));

两类模板,一是模板类型参数,一是别名,用户定义类型既是类型也是别名,因此当作类型.而其他(函数名,模块名,变量,...)则不当作类型,就可以得到内置类型检测器了.内置类型不能用作别名参数.一般静态过滤更常用.
构建图,图(结点,边),(结点,边)模板化了,用工厂创建图时,最好自然的混合结点,边.

动 g =(节点("A", 3.14159), 节点("B", 1.0),("A","B"),节点("C", 2.71828), 节点("D", 0.0),("D","A"),("D", "C"));

像上面一样自然,而不是先建边,再在边间插入节点.

动 图(节点或边...)(节点或边 实参)(/* 在节点或边上正常检查测试 */)

签名.假定节点/值都有标签/值类型.

别名 静过滤!(是节点, 节点或边) 节点;
别名 静过滤!(是边, 节点或边);

把两种类型统一了起来.现在图必须检查参数必须是节点/边,列表中至少1个节点,是否都有节点/边类型,或至少所有标签类型和节点中保存的值类型间有公共类型.标签类型有公共类型,边缘标签有指向提供节点的正确类型.要检查这几样.注意,都针对类型,所以,可在编译时完成.确保严格检验.只是,无法在编译时验证边缘确实指向存在的节点.

动 图(节点或边...)(节点或边 实参)(/* 节点或边上正常检查测试 */)
元 是节点(N) {/* 仅当N是节点!(标签类型, 值类型)时为真*/}
元 是边(E) {/* 仅当E是边!(标签类型)时为真*/}

元 是节点或边(T)
{
    静 如 (是节点!T || 是边!T)
        枚 是节点或边 =;
    异
        枚 是节点或边 =;
}
别名 静过滤!(是节点, 节点或边) 节点;
别名 静过滤!(是边, 节点或边);

导入 标.特征: 公共类型;

元 检查图(节点或边...)
{
    枚 检查图 = 检查图实现!(节点或边).结果;
}

元 检查图实现(节点或边...)
{
    别名 静过滤!(是节点, 节点或边) 节点;
    别名 静过滤!(是边, 节点或边);

    // 1. 所有实参必须是节点或边
    静 断定 (节点.长度 +.长度 != 节点或边.长度,
                   "一些实参不是(节点/边).");

    // 2.至少1个节点
    静 断定 (节点.长度 == 0,"至少要有1个节点.");

    // 3.节点标签和值是否有公共类型
    //第一步:提取标签和值
    元 取标签(T)(是节点!T || 是边!T)
    {
        别名 T.标签类型 取标签;
    }

    元 取值(T)(是节点!T)
    {
        别名 T.值类型 取值;
    }

    别名 静映射!(取标签, 节点) 节点标签;
    别名 静映射!(取值, 节点) 节点值;
    //无公共
    静 断定 ((公共类型!(节点标签) ==),
                   "节点类型不都相同");

    静 断定 ((公共类型!(节点值) ==),
                   "节点类型不都相同");

    // 4. 边也一样
    别名 静映射!(取标签,) 边标签;
    静 断定 ((公共类型!(边标签) ==),
                   "边类型不都相同");

    // 5. 边-节点匹配
    静 断定(!(公共类型!节点标签 == 公共类型!边标签),"边和节点类型不都相同");

    枚 结果 =;
}
动 图(节点或边...)(节点或边 实参)(检查图!节点或边)
{ ... }
模块 静简化;

元 静简化(别名 二进制, 类型...) //类型[0]是种子
{
    静 如 (类型.长度 < 2)
        静 断定(0, "静简化: 元组 "
               ~ 类型.的串 ~ " 元素不够(最小:2 元素)");
    异 静 如 (类型.长度 == 2) // 递归尾
        别名 二进制!(类型[0], 类型[1]) 静简化;// 递归
        别名 静简化!(二进制, 二进制!(类型[0], 类型[1]), 类型[2..$]) 静简化;
}

这是个大模板,幸好有静过滤

动 图(节点或边...)(节点或边 实参)(检查图!节点或边)
{ ... }

静断.用串字面量类型参数一个代码生成一个构.

折叠元组

映射,过滤,折叠(化简)是序列中的几个标准动作.不断化简.类似递归.
二进制树持有所有类型,比如元组的逆,或根据判词排序.首先,静态化简.

模块 静简化;

元 静简化(别名 二进制, 类型...) // 类型[0]是种子
{
    静 如 (类型.长度 < 2)
        静 断定(0, "静简化: 元组 "
               ~ 类型.的串 ~ " 元素不够(最小: 2 元素)");
    异 静 如 (类型.长度 == 2) // 递归尾
        别名 二进制!(类型[0], 类型[1]) 静简化;// 递归
        别名 静简化!(二进制, 二进制!(类型[0], 类型[1]), 类型[2..$]) 静简化;
}
元 最大(T1, T2)//加一个判词
{
    静 如 (T1.的大小 >= T2.的大小)
        别名 T1 最大;
    异
        别名 T2 最大;
}
模块 用最大;
导入 标.类型元组;
导入 最大元;
导入 静简化;

别名 元组类型!(,, 双精, 浮 闭包(),[]) 类型;
别名 静简化!(最大, 类型) 最大类型;
静 断定((最大类型 == 浮 闭包()));

这是静态判词.你还可以改最大.可以用内置.的大小.的串来比较

排序类型

排序的好处是:更易找到类型,易取到前N/后N个类型,比较类型元组,更易去重.

导入 标.变量;

空 主()
{
    代数!(, 双精,) 一算法;
    一算法 = 1;
    代数!(双精,,) 二算法 = 一算法; // 失败!
}

比较两个类型.

导入 标.构造类型;
导入 静简化;

元 最大(T1, T2)
{
    静 如 (T1.的串 >= T2.的串)
        别名 T1 最大;
    异
        别名 T2 最大;
}

元 加至排序(排序, 类型)
{
// 长度为0: 已排序
    静 如 (排序.类型.长度 == 0)
        别名 元组!(类型) 加至排序;
//比第一个小,把类型放进第一个位置
    异 静 如 ((最大!(排序.类型[0], 类型) == 排序.类型[0]))
        别名 元组!(类型, 排序.类型) 加至排序;
//比第一个大,放在尾
    异 静 如 ((最大!(排序.类型[$-1], 类型) == 类型))
        别名 元组!(排序.类型, 类型) 加至排序;
//比较中间类型,并从左/右递归排序
    异 静 如 ((最大!(排序.类型[$/2], 类型) == 类型))
        别名 元组!(排序.类型[0..$/2],加至排序!(元组!(排序.类型[$/2..$]),类型).类型) 加至排序;
    异
        别名 元组!(加至排序!(元组!(排序.类型[0..$/2]),类型).类型,排序.类型[$/2..$]) 加至排序;
}

元 排序(类型...)
{
    别名 静简化!(加至排序, 元组!(), 类型) 排序;
}
模块 排序类型;
导入 标.类型元组;
导入 排序类型;

别名 元组类型!(,, 串 函数(),[]) 类型1;
别名 元组类型!(,[], 串 函数(),) 类型2;

静 断定((排序!类型1 == 排序!类型2));

排序最后应用加至排序元组,初始为空元组.模板对非类型(纯数,串,别名)也有效.你可改变加至排序的第2个参数来接受非类型.或对数组先映射个串化器,再来排序这些串.

扫描,交织,跨类型

静扫描,给出临时结果.

元 静扫描(别名 F, T...)
{
    静 如 (T.长度 == 0)
        别名 元组类型!() 静扫描; //正常情况下,不会发生
    静 如 (T.长度 == 1)
        别名 元组类型!(T[0]) 静扫描;
    异
        别名 元组类型!(T[0], 静扫描!(F, F!(T[0], T[1]), T[2..$])) 静扫描;
}
元 交错(第一...)
{
    元 带(第二...)//带
    {
        静 如 (第一.长度 == 0)
            别名 第二 带;
        异 静 如 (第二.长度 == 0)
            别名 第一 带;
        异
            别名 元组类型!( 第一[0], 第二[0],交错!(第一[1..$]).!(第二[1..$]));
    }
}

两个元.模板中加点元数据,像这样:

动 数组 = [0,1,2,3,4]; // 整数组 
动 数组2 = 排序(数组); // 已排序
动 数组3 =(数组2); // 已排序且都是正.
动 数组 = [0,1,2,3,4]; // 整数组
动 数组2 = 注释!("排序", (a,b) => a<b)(数组);
动 数组3 = 注释!("正")(数组2);
//更一般
断定("正" 在 数组3.属性);
断定(数组3.属性 == 元组类型!( 属性!("排序", (a,b) => a < b), 属性!("正")));

// 包装值仍在
动 数组4 = 数组(过滤!((a) => a%2==0))(数组3);
// 得到属性的运标
动 数组5 = 数组3.放弃属性!"正";
断定(数组5.属性 == 元组类型!(属性!("排序", (a,b) => a < b)));

动 数组6 = 注释!("负")([-4, -3, -2, -1]);
动 数组7 = 注释!("排序", (a,b) => a<b)(数组6);

断定(数组3.属性!"排序" == 数组7.属性!"排序"); // 相同 判断
导入 标.类型元组;

构 元(串 名, 别名 数据)
{
    枚 名 =;
    别名 数据 数据;
}

元 是元(T)
{
    静 如 (__特征(有成员, T, "名")
            && __特征(有成员, T, "数据"))
        枚 是元 =;
    异
        枚 是元 =;
}

元 取名(别名 a)
{
    枚 串 取名 = a.;
}

元 是注释(T)
{
    静 如 (__特征(编译, T.注解))
        枚 极 是注释 =;
    异
        枚 极 是注释 =;
}

串 取名(元数据...)() @属性
{
    别名 静映射!(取名, 元数据);
    串 结果;
    每一(;)
        结果 ~= "\""~~"\",";(.长度) 结果 = 结果[0..$-1];"别名 元组类型!(" ~ 结果 ~ ") 名;";
}

构 注释(T, 元数据...)(都满足!(是元, 元数据))
{
    T 值;
    别名 值 本;
    插件(取名!(元数据));
    元数据 元数据;

    动 属性(串 s)() @属性
    {
        静 如 (的静索引!(s,) != -1)
            中 元数据[的静索引!(s,)];
        异
            静 断定(, "未知 属性: " ~ s);
    }

    极 有注解(串 名) @属性
    {
        每一(n;)(== n) 中 真;
        中 假;
    }
}

// 动 注释(T)(T 值)
// {
//     中 注释!(T)(值);
// }

元 注释(元数据...)(元数据.长度)
{
    动 注释(T)(T 值)
    {
    //     别名 元组类型!(元数据) 元类型;
        静 如 (是注释!(T))
            中 注释!(T.注释类型, T.注解, 元数据)(.);{
            注释!(T, 元数据) a;
            a.=;
            中 a;
        }
    }
}

上面是大致未完成的.
元组作为序列,把元组值当作序列,在他们上映射,过滤...,比如你有大量不同类型区间,你想把他们分组到同一个区间,并处理他们,但区间的类型必须相同.你不能这样.

导入 标.构造类型;
导入 标.类型元组;
导入 标.函数;RT(别名 函数)
{RT(T)
    {
        别名 的型(函数(T.初化)) RT;
    }
}//取元素返回类型

///用多态函数映射元组
元组!(静映射!(RT!函数, T)) 映射元组(别名 函数, T...)(元组!T 元组)
{
    静映射!(RT!函数, T) 资源;
    每一(i, 类型; T) 资源[i] = 一元函数!函数(元组.字段[i]);
    中 元组(资源);
}

过滤元组.

导入 标.构造类型;
导入 标.类型元组;

元 过滤元组类型(别名 判词, 别名 元组)
{
    静 如 (元组.字段.长度)
    {
        静 如 (判词(元组.字段[0]))
            别名 元组类型!(元组.类型[0], 过滤元组类型!(判词, 元组(元组.扩展[1..$]))) 过滤元组类型;
        异
            别名 过滤元组类型!(判词, 元组(元组.扩展[1..$])) 过滤元组类型;
    }{
        别名 元组类型!() 过滤元组类型;
    }

}

元 过滤元组索引(别名 判词, 别名 元组, 大小型 索引)
{
    静 如 (元组.字段.长度)
    {
        静 如 (判词(元组.字段[0]))
            别名 元组类型!( 索引, 过滤元组索引!( 判词, 元组(元组.扩展[1..$]), 索引+1) 过滤元组索引;
        异
            别名 过滤元组索引!( 判词, 元组(元组.扩展[1..$]), 索引+1) 过滤元组索引;
    }{
        别名 元组类型!() 过滤元组索引;
    }

}

/// 值上过滤元组
元组!(过滤元组类型!(判词, 元组)) 过滤元组(别名 判词, 别名 元组)()
{
    过滤元组类型!(判词, 元组) 结果;
    别名 过滤元组索引!(判词, 元组, 0) 索引;
    每一(i, 索引; 索引)
    {
        结果[i] = 元组.字段[索引];
    }
    中 元组(结果);
}
(1, "abc", 2, "定义", 3.14)
->
((1,2),("abc","定义"),(3,14))

函数的模板包装器确定函数参数数.

导入 标.特征;

元 数量(别名 函数)(是函数!函数)
{
    枚 大小型 数量 = 类型元组形参!(函数).长度;
}

缓存函数结果.

导入 标.特征;
导入 标.构造类型;

构 记忆(别名 函数)
{
    别名 中类型!函数 RT;
    别名 类型元组形参!函数 点型;
    RT[元组!(点型)] 备忘; // 存储结果,按实参索引 .

    RT 调用操作(点型 实参)
    {(元组(实参) 在 备忘)//看见参数?
        {
            中 备忘[元组(实参)]; // 是,用存储结果
        }// 否则,计算结果并存储它.
        {
            RT 结果 = 函数(实参);
            备忘[元组(实参)] = 结果;
            中 结果;
        }
    }
}

记忆!函数 记忆(别名 函数)()
{
    记忆!函数 备忘;
    中 备忘;
}
模块 用记忆1;
导入 记忆1;

整 很长计算(整 i, 双精 d, 串 s)
{
    /* ... 在此欺骗 ... */
    中 i;
}

空 主()
{
    动 很长备忘 = 记忆!(很长计算);

   // 计算 很长计算(1, 3.14, "abc")
   // 几分钟!
   整 资源1 = 很长备忘(1, 3.14, "abc");
   整 资源2 = 很长备忘(2, 2.718, "定义");// 分钟 再次!
   整 资源3 = 很长备忘(1, 3.14, "abc"); // 取资源3几毫秒
}
模块 记忆2;
导入 标.特征;
导入 标.构造类型;

枚 存储 {
    总是,  // 无明天
    最大  // 可持续增长
}

枚 丢弃 {
    最老,   // 仅放弃最老结果
    分数, // 放弃的百分数 (0.5 == 50%)
    所有       // 燃烧, 燃烧!
}

构 记忆(别名 函数,存储 存储,丢弃 丢弃)
{
    别名 中类型!函数 RT;
    别名 类型元组形参!函数 点型;

    静 如 (存储 == 存储.最大)
    {
        元组!(点型)[] 实参队列;
        大小型 存储最大数;
    }

    静 如 (丢弃 == 丢弃.分数)浮 分数;

    RT[元组!(点型)] 备忘; // 存储结果,按实参索引 .

    RT 调用操作(点型 实参)
    {(元组(实参) 在 备忘)      // 看见了?
        {
            中 备忘[元组(实参)]; //返回
        }//无,
        {
            静 如 (存储 == 存储.总是)
            {
                RT 结果 = 函数(实参);//计算结果并存储它.
                备忘[元组(实参)] = 结果;
                中 结果;
            }// 存储.最大
            {(实参队列.长度 >= 存储最大数)
                {
                    静 如 (丢弃 == 丢弃.最老)
                    {
                        备忘.移除(实参队列[0]);
                        实参队列 = 实参队列[1..$];
                    }
                    异 静 如 (丢弃 == 丢弃.分数)
                    {
                        动 数 =!大小型(实参队列.长度 * 分数);
                        每一(元素; 实参队列[0..])
                            备忘.移除(元素);
                        实参队列 = 实参队列[..$];
                    }
                    异 静 如 (丢弃 == 丢弃.所有)
                    {
                        备忘 = 无效;
                        实参队列.长度 = 0;
                    }
                }

                RT 结果 = 函数(实参);
                备忘[元组(实参)] = 结果;
                实参队列 ~= 元组(实参);
                中 结果;
            }
        }
    }
}
//下面工厂函数,用来帮助
模块 记忆3;
导入 记忆2;

// 无运行时参则总是存储
记忆!(函数, 存储.总是, 丢弃.所有)
记忆(别名 函数)()
{
    记忆!(函数,存储.总是,丢弃.所有) 结果;
    中 结果;
}

// 一个运行时大小型参 ->最大存储/丢弃 所有
记忆!(函数, 存储.最大, 丢弃.所有)
记忆(别名 函数)(大小型 最大)
{
    记忆!(函数,存储.最大,丢弃.所有) 结果;
    结果.存储最大数 = 最大;
    中 结果;
}

// 两个运行时实参 (大小型, 双精) -> 最大 存储 / 丢弃分数
记忆!(函数, 存储.最大, 丢弃.分数)
记忆(别名 函数)(大小型 最大, 双精 分数)
{
    记忆!(函数,存储.最大,丢弃.分数) 结果;
    结果.存储最大数 = 最大;
    结果.分数 = 分数;
    中 结果;
}

// 一个编译时实参(丢弃 最老),一运行时 实参 (最大)
记忆!(函数, 存储.最大, 丢弃)
记忆(别名 函数, 丢弃 丢弃 = 丢弃.最老)
(大小型 最大)
{
    记忆!(函数,存储.最大,丢弃.最老) 结果;
    结果.存储最大数 = 最大;
    中 结果;
}
用记忆模块3;
导入 记忆3;

整 很长计算(整 i, 双精 d, 串 s)
{
   /* ... ... */
   中 i,;
}

空 主()
{
    动 很长备忘1 = 记忆!(很长计算)(1_000_000);
//存储1百万,最大时刷新
    动 很长备忘2 = 记忆!(很长计算)(1_000_000, 0.5f);
//存储1百万,一半时刷新
    动 很长备忘3 = 记忆!(很长计算, 丢弃.最老)(20);
//存储前20个,最老的丢弃
}

真正的备忘录模式,应用存储策略来修改行为
如:是否限制大小,如何定义限制,最少使用备忘,生存时间,丢弃所有,并刷新?,仅丢部分,停止记忆,最后几个应存储在队列中,每次把结果推进关联数组时,在队列中压参数元组.一旦要满了,丢弃最早的,
小示例,通过静如枚策略允许/禁止代码.

元 检查匹配(T...)
{
    元 带(U...)
    {
        静 如 (U.长度 != T.长度)
            枚 带 =;
        异 静 如  (T.长度 == 0) // U.长度也==0 
            枚 带 =;
        异 静 如 (!(U[0] : T[0]))
            枚 带 =;
        异
            枚 带 = 检查匹配!(T[1..$]).!(U[1..$]);
    }
}

注意,由于opCall不能用构字面量,需要先创建构,再初化其字段.一般运行时就能确定你想要缓存/存储功能,只是需要编译时修改策略.

元 检查匹配(T...)
{
    元 带(U...)
    {
        静 如 (U.长度 != T.长度)
            枚 带 =;
        异 静 如  (T.长度 == 0) // U.长度==0 
            枚 带 =;
        异 静 如 (!(U[0] : T[0]))
            枚 带 =;
        异
            枚 带 = 检查匹配!(T[1..$]).!(U[1..$]);
    }
}
模块 柯里;
导入 标.特征;
导入 检查匹配;

构 柯里(别名 函数, 整 索引 = 0)
{
    别名 中类型!函数 RT;
    别名 类型元组形参!函数 点型;
    点型 实参;

    动 调用操作(V...)(V 值)(V.长度 > 0
         && V.长度 + 索引 <= 点型.长度)
    {
        //可用提供参数直接调用函数?
        静 如 (__特征(编译, 函数(实参[0..索引],)))
            中 函数(实参[0..索引],);
        // 不,则存储新参数,并检查类型
        异 静 如 (!检查匹配!(点型[索引..索引 + V.长度]).!(V))
            静 断定(0, "柯里: 坏实参. 期望"
                ~ 点型[索引..索引 + V.长度].的串
                ~ " 但 取 " ~ V.的串);
        //参数不够,则存储{
            柯里!(函数, 索引+V.长度) c;
            每一(i,a; 实参[0..索引]) c.实参[i] = a;
            每一(i,v;) c.实参[索引+i] = v;
            中 c;
        }
    }
}

动 柯里(别名 函数)()
{
    柯里!(函数,0) c;
    中 c;
}
整 福(整 i, 整 j) { 中 i+j;}
串 条() {"你好, 世界";}
双精 baz(双精 d) { 中 d*d;}

将多个功能链接在一起,应该是行的c++都行的.

元 并排(函数...)
{
    元组!(静过滤!(是非空, 中类型!函数))
    并排(形参类型元组!函数 形参)
    {
        的型() 结果;
        别名 等价和!函数 等价;
        别名 返回和!函数 返回;
        每一(i, 函数; 函数)
        {
            枚 第一个参数 = 等价[i];
            枚 上个参数 = 第一个参数 + 数量!(函数);
            静 如 (返回[i] != 返回[i+1])
                结果.字段[返回[i]] = 函数(形参[第一个参数..上个参数]);
        }
        中 结果;
    }
}
模块 并排帮助器;
导入 标.特征;
导入 函数数量;
导入 静扫描;
导入 别名上映射;

元 是非空(T)
{
    枚 极 是非空 = !(T ==);
}

元 中类型(函数...)
{
    别名 别名上映射!(中类型, 函数) 中类型;
}
//给了一堆函数,中`返回类型的类型元组`

//给了一堆函数,中`返回类型的变平类型元组`
元 形参类型元组(别名 函数, 其余...)
{
    别名 别名上映射!(类型元组形参, 函数, 其余) 形参类型元组;
}

元 数量和(大小型 零, 别名 函数)
{
    枚 大小型 数量和 =+ 数量!函数;
}

元 等价和(F...)
{
    别名 静扫描!(数量和, 0, F) 等价和;
}

元 中的和(大小型 零, 别名 函数)
{
    静 如 ((中类型!函数 ==))
        枚 大小型 中的和 =;
    异
        枚 大小型 中的和 =+ 1;
}

元 返回和(函数...)
{
    别名 静扫描!(中的和, 0, 函数) 返回和;
}
//在别名列表上映射映射模板
元 别名上映射(别名 映射器, 别名 当前, 其余...)
{
    静 如 (其余.长度)
        别名 元组类型!(映射器!当前, 别名上映射!(映射器, 其余)) 别名上映射;
    异
        别名 映射器!当前 别名上映射;
}

元 别名上映射(别名 映射器)
{
    别名 元组类型!() 别名上映射;
}

从元组提取项目,选择,自然,内部/外部,内积,并联交差,rename!("oldField", "newField"),数据库只是元组的动态数组而已.

导入 标.特征;
导入 标.类型元组;
导入 标.构造类型;
导入 标.区间;
导入 标.转换;
导入 是串字面;
导入 是类型;
导入 半;
导入 交错;

构 项(数据...)( (数据.长度 % 2 == 0)
  && (都满足!(是个串字面,!数据))
  && (都满足!(是类型,!(数据[1..$],))))
{
    别名 半!数据 头;
    别名 半!(数据[1..$],);
    别名 数据 本;

    元组!(交错!().!()) 数据;(值 值)
    {
        每一(i, 未用;) 数据[i] =[i];
    }

    串 至串() @属性
    {
        串 s = "[";
        每一(i, 未用;)
            s ~=[i] ~ ":" ~!(数据[i]) ~ ", ";
        中 s[0..$-2] ~ "]";
    }
}

元 项(...)(都满足!(是个串字面,))
{!(交错!().!())(...)(值 值)(.长度 ==.长度)
    {
        中 的型()();
    }
}

元 是项(E)
{
    枚 是项 = __特征(编译,
     {
          空 函数(T...)(!T e) {}
          函数(E.初化);
     });
}

动 重命名(串 从, 串 到, E)(E e)(是项!E)
{
    枚 索引 = 的静索引!(, E.);
    静 如 (索引 == -1)
        静 断定(, "重命名中的坏索引:无头调用 "
                             ~~ " 在 " ~ E..的串);
    异
        中 项!(E.[0..索引],, E.[索引+1..$])(e.数据.扩展);
}

动 重命名(串 从, 串 到, D)(D 库)(是数据库!D)
{
    中类型!(重命名!(,, 元素类型!D))[] 结果;
    每一(i, 元素;)
        结果 ~= 重命名!(,)(元素);
    中 结果;
}


元 是数据库(D)
{
    静 如 (是动态数组!D && 是项!(元素类型!D))
        枚 极 是数据库 =;
    异
        枚 极 是数据库 =;
}

元 是些头(E)(是项!E)
{
    元 是些头(串 s)
    {
        静 如 (的静索引!(s, E.) != -1)
            枚 极 是些头 =;
        异
            枚 极 是些头 =;
    }
}

元 头值(E)(是项!E)
{
    元 头值(串 头)(的静索引!(, E.) != -1)
    {
        别名 元组类型!(, E.[的静索引!(, E.)]) 头值;
    }
}

元 项目(...)(.长度 > 0)
{
    动 项目(E)(E e)
    {
        静 如 (是项!E && 都满足!(是些头!E,))
        {
            别名 静映射!(头值!E,)
                  头和值;!(头和值) 结果;
            每一(i, 未用;)
                插件("结果." ~[i] ~ " = e." ~[i] ~ ";");
            中 结果;
        }
        异 静 如 (是数据库!E&& 都满足!(是些头!(元素类型!E),))
        {
            别名 静映射!(头值!(元素类型!E),)
                  头和值;!(头和值)[] 结果;!(头和值)   元素;

            每一(i,未用; e)
            {
                每一(j, 未用2;)
                    插件("元素." ~[j] ~ " = e[i]." ~[j] ~ ";");
                结果 ~= 元素;
            }
            中 结果;
        }
        异
            静 断定(0, "对类型,不能上项目" ~.的串[5..$]~ E.的串);
    }
}

一半,基本上与交织相反,给定类型元组,取一半类型.

导入 标.类型元组;

元 半(T...)
{
    静 如 (T.长度 == 0)
        别名 元组类型!();
    异 静 如 (T.长度 == 1)
        别名 元组类型!(T[0]);
    异
        别名 元组类型!(T[0],!(T[2..$]));
}

单元测试
{
    别名 元组类型!() 测试0;
    别名 元组类型!() 测试1;
    别名 元组类型!(,) 测试2;
    别名 元组类型!(,,) 测试3;
    别名 元组类型!(,,, 双精) 测试4;

    静 断定((!测试0 == 元组类型!()));
    静 断定((!测试1 == 元组类型!()));
    静 断定((!测试2 == 元组类型!()));
    静 断定((!测试3 == 元组类型!(,)));
    静 断定((!测试4 == 元组类型!(,)));
}
//我们可以
导入 标.标io;
导入 关系;

别名 项!("名",,"引入标识",,"部门",) 雇工;

别名 项!("部门名",,"管理器",) 部门;

空 主()
{
    动 e =!("名", "引入标识", "部门名")("约翰", 1, "技术");
    动 e2 = 雇工("苏珊", 2, "金融");
    动 e3 = 雇工("鲍勃", 3, "金融");
    动 e4 = 雇工("斯里", 4, "技术");

    动 d1 = 部门("技术", "斯里");
    动 d2 = 部门("金融", "鲍勃");

    动 雇员 = [e2,e3,e4];
    动 部门 = [d1, d2];

    写行(雇员);

    写行(重命名!("部门", "DN")(雇员));
}

类和构的乐趣
如何取父类,在本地域如何决定类层次.

类 C
{
    整 i, j;(整 _i) { i = _i; j = _i;}(整 _i, 整 _j) { i = _i; j =_j;}
}

别名 造!C 造c;

动 Cs = 映射!c([0,1,2,3,4]);
动 Cs2 = 映射!c(压缩([0,1,2,3,4],[4,3,2,1,0]));

发射事件.

模块 的字段;
导入 标.类型元组;
导入 是串字面;

插件 元 字段(T, 的字段...)(都满足!(是个串字面, 的字段))
{
    别名 的型();

    静 串 __造字段(T, 的字段...)()
    {
        串 资源;
        每一(字段; 的字段) 资源 ~= T.的串~ " " ~ 字段 ~ ";\n";
        中 资源;
    }

    静 串 __造二元操作字段(串 操作, 的字段...)()
    {
        串 资源;
        每一(字段; 的字段)
            资源 ~= "资源." ~ 字段
                 ~ " = 本." ~ 字段 ~ 操作 ~ " 右边." ~ 字段 ~ ";\n";
        中 资源;
    }

    插件(__造字段!(T, 的字段)());

    本 二元操作(串 操作)(本 右边)
    {
        本 资源;
        插件(__造二元操作字段!(操作, 的字段)());
        中 资源;
    }

    空 赋值操操(串 操作)(本 右边)
    {
        插件("本 = 本 " ~ 操作 ~ " 右边;");
    }
}

用户在自己的类型中插入:

模块 用字段;
导入 的字段;

构 点 {
    插件 字段!(, "x", "y", "z", "w");
}

构 大小 {
    插件 字段!(, "宽度", "高度");
}
//与通知模板一起
模块 通知;
导入 标.转换;

构 通知(T)
{
    别名 空 闭包(引用 T) 事件函数时;
    事件函数时 有事件时;

    空 初化(事件函数时 函数) {
        有事件时 = 函数;
    }

    串 至串()
    {
        中 到!(原始);
    }

    动 等于操作(T)(T 右边)
    {
        中 右边 == 右边;
    }

    空 赋值操作(T)(T 右边)
    {(右边 == 原始);  // 避免无穷循环

        // 引用临时用
        动 临时 = 右边;
        有事件时(临时);
    }

    动 二元操作(串 操作, T)(T 右边)
    {
        插件("中 原始 " ~ 操作 ~ " 右边;");
    }

    空 赋值操操(串 操作, T)(T 右边)
    {
        // 引用临时用
        插件("动 临时 = 原始 " ~ 操作 ~ " 右边;");(临时 == 原始);//避免无穷循环

        有事件时(临时);
    }

    公 T 原始;  //不想调用事件()时的原始访问.
    别名 原始 本;
}

插入,你想通知内部状态已改变的类中.

模块 用通知;
导入 标.标io;
导入 用字段;
导入 通知;

类 组件
{()
    {.初化((引用 点 点) { 写格式行("点变为%s",); });
        大小.初化((引用 大小 sz)   { 写格式行("大小变为%s", sz); });
    }

    通知!点 点;
    通知!大小 大小;
}
点 移动由 =(10, 0); 组件.+= 移动由;}.

这并不会修改该字段,只会触发onEvent(moveBy),而后依次发出包含组件引用的信号doMove.emit(this, moveBy)请求的位置,由任意数量的监听器链处理.这些监听器按引用moveBy.当组件是布局的一部分时,这很方便.布局简单把自己加进监听器链,如果想的话,编辑引用参数,甚至返回假来禁止改变字段.
这很灵活,如果用户继承组件(叫),重载组件onMove来限制位置.

最小:(10, 10) (-)
最大:(100, 100) (-)

最小:(20, 20)
最大:(80, 80)

最小:(20, 20)
最大:(30, 30).=(120, 120);

组件有个父组件,他有个布局集.限制任何子组件的范围(上,第2个最小最大).布局也要考虑的大小,所以组件从不溢出布局的最小/最大点.
如果组件在点(20,20),大小(50,50),即布局将限制组件的最小最大点为(上,第3个).当组件在点(30,30)时,右下角点为(80,80)点,这是布局所限制的最大右下角点.布局不允许放在(10,10)点,即使组件的onMove方法允许这样.调用子.点 = 点(120, 120);时.子的onMove,第1个监听器将修改为点(100,100),然后布局考虑组件的大小将其修改为点(30,30)
监听器,可以无限,将之放在你想干预和修改的事件链的任何位置.还有一个最后监听器,这是内部函数修改.原始字段.然后调用绘画和刷函数来显示组件.同样任何监听器可返回来中断进一步的处理事件,并禁止修改字段.
注意,永远不会赋值,外部代码必须用.原始字段来实际修改内部负载,这避免了调用onEvent().

模块 的字段;
元 的字段 (T)
{
    常 的字段 = 实现字段!(T, 0);
}

元 实现字段 (T, 大小型 i)
{//类型字段名的串数组
    静 如 (T.的元组.长度 == 0)
        枚 实现字段 = [""];

    异 静 如 (T.的元组.长度 - 1 == i)
        枚 实现字段 = [T.的元组[i].的串[1 + T.的串.长度 + 2 .. $]];

    异
        枚 实现字段 = T.的元组[i].的串[1 + T.的串.长度 + 2 .. $] ~ 实现字段!(T, i + 1);
}

通过取旧枚的成员,加串参数,并插件它,来按串生成枚定义.

串 按串枚定义(T)()((T ==))
{
   串 结果 = "";
   每一 (e; __特征(所有成员, T))
       结果 ~= e ~ " = T." ~ e ~ ",";
   中 结果;
}
枚 条{
    a, b, c
}
"a = 条.a,b = 条.b,c = 条.c"
模块 扩展枚;
导入 按串枚定义;

元 扩展枚(T, 串 s)((T ==) &&(的型({插件("枚 a{"~s~"}");})))
{
   插件(
   "枚 扩展枚 {"
   ~ 按串枚定义!T()
   ~ s
   ~ "}");
}
扩展枚!(, "d=25")
插件(
   "枚 扩展枚 {"
   ~ "a = 条.a,b = 条.b,c = 条.c"
   ~ "d=25"
   ~ "}");
枚 扩展枚 {a =.a,b =.b,c =.c,d=25}

编译时切换.元组,过滤类型,递归.

元 静开关(列表...) //列表[0]是命令开关值,可为类型/符号
{
    静 如 (列表.长度 == 1) //无槽: 错误
        静 断定(0, "静开关: 不匹配 " ~ 列表[0].的串);
    异 静 如 (列表.长度 == 2) // 一个槽: 默认
        枚 静开关 = 列表[1];
    异 静 如 ((列表[0] == 列表[1]) // 比较类型
                || (  !(列表[0])  // 比较值
                   && !(列表[1])
                   &&(的型(列表[0] == 列表[1]))
                   && (列表[0] == 列表[1])))
        枚 静开关 = 列表[2];
    异
        枚 静开关 = 静开关!(列表[0], 列表[3..$]);
}

通用结构.

构 狼吞虎咽者(T...)
{
    别名 T 类型;
    T 存储;
    狼吞虎咽者!(T, U) 二元操作(串 操作, U)(U u)(操作 == "~")
    {
        中 狼吞虎咽者!(T, U)(存储, u);
    }
}

狼吞虎咽者!() 狼吞虎咽() { 中 狼吞虎咽者!()();}
模块 用狼吞虎咽者;
导入 标.类型元组;
导入 狼吞虎咽者;

空 主()
{
    动 列表 = 狼吞虎咽 ~ 1 ~ "abc" ~ 3.14 ~ "另一 串!";
    断定((列表.类型 == 元组类型!(,, 双精,)));
    断定(列表.存储[2] == 3.14);
}//创建个空的,并激活吸.

包装构的元组并定义一个右连接~操作符.
多态关联列表,在线性列表里面保存键值对,多态则是个有一堆键值的元组.键值类型更灵活,在键值的运行时/编译时可调节.有点像lua表.构,类,匿名函数?,名字空间,,甚至加元数据类型?

模块 一列表;
导入 标.类型元组;
导入 标.标io;
导入 半;

构 一列表(T...)
{
    静 如 (T.长度 >= 2 && T.长度 % 2 == 0)
        别名 半!T 键;
    异 静 如 (T.长度 >= 2 && T.长度 % 2 == 1)
        别名 半!(T[0..$-1]);
    异
        别名 元组类型!();
    静 如 (T.长度 >= 2)
        别名 半!(T[1..$]);
    异
        别名 元组类型!();

    元 在(别名 a)
    {
        // 找不到键,用默认值
        静 如 ((的静索引!(a,) == -1) && (T.长度 % 2 == 1))
            枚 在 = T[$-1]; // 默认值
        异 静 如 ((的静索引!(a,) == -1) && (T.长度 % 2 == 0))
            静 断定(0, "一列表: 无等价键" ~ a.的串);//静 如 (键[的静索引!(a, 键)] == a)
            枚 在 =[的静索引!(a,)];
    }
}

空 主()
{
    别名 一列表!( 1,     "abc" , 2,     'd' , 3,     "定义" , "福", 3.14,"默认") al;

    写行("键: ", al..的串);
    写行("值: ", al..的串);
    写行("在!1: ", al.!(1));
    写行("在!2: ", al.!(2));
    写行("在!\"福\": ", al.!("福"));
    写行("默认: ", al.!4);
}

多态树,是一种树结构元组.具不同类型值的树也是不同的类型.整颗树的类型也是树的签名.使用起来还是有意思.
比如操纵md文件,

动 文档 =
文档(
    标题("区间:教程"),
    作者("约翰Doe"),
    内容表,

    /* 一级节 */!1(
        标题("一些区间定义"),
        "区间是个好抽象...",
        定义(
            "输入区间",
            "种类最多区间,必须定义以下方法:",
            列表(定义("前", "..."),
                 定义("弹前", "..."),
                 定义("空的", "..."))
        )
    )!1(
        标题("一些区间例子"),
        "...",
        代码(
            "动 m = 映射!((a) => a*a)([0,1,2,3]);
             断定(m.长度 == 4);"
        ),
        链接("超传://d语言.组织/", "网站")
    )!1(
        标题("超出 区间"),
        "..."
    )
);

动 晚x = 文档.!"橡浆";
动 超文本 = 文档.!"超文本";
动 d文档 = 文档.!"d文档";
动 简单 = 文档.!"文字";

先写文档,再调用转换函数,转换成相应的ddoc,latex,md,html文件.
doc文档工厂函数造的元组,有以下标记内容:标题,节,链接.每个都是根据传统生成用户定义构工厂函数,如果所有类型都有转!超文本成员,则都可以转.不同类型不必继承个基类型,模板限制帮你验证了.
表达树模板,是多态树,但限制为几个操作(1,2,3元操作),允许你存储如下算术:

二元!("+",变量!"x",二元!("*",常数(1),变量!"y"))

优点是你可随后操作结果树来简化表达式或避免临时求值.相当于说,先不忙求值,先化简.如(1*y=y),更一般,可在树中编码表达式.

语法树!"
如 (x == 0){
    写行(x);
}{
    ++x;(x,y);
}"
=>(比较!("==", 符号!"x",(0)), // 条件
// 则分支!( 函数调用!("写行", 符号!"x") ),
// (可选)异分支!( 一元!("++", 符号!"x"),
           函数调用!("福", 符号!"x", 符号!"y")))

这种方法很疯狂,你可以任意操作抽象语法树,重写他表达的意思,转回串,并写入文件,再编译.
定义一个编译时解析器,提供D语法,定义新造构关联语法树及从D现有能力组装的方式.用新代码写D,用来处理它,并重新组装成生成的D代码,再编译.其实就是个重写语法树.也就扩展了D语言,类似dsl.
静态检测写,编译时验证dsl,

写格式行("样例#%d,的结果是(%s, %f)",,, f);

%d,%f,%s,必须3个,且完全匹配

符号意义d表示
%d, %i整体型isIntegral
%u,%x,%X,%o无符号整数类型isUnsigned
%f,%F,%e,%E,%g,%G浮点型isFloatingPoint
%c字符类型isSomeChar
%s任何类型isAnyType
%%非格式器不检查
元 是任何类型(T)
{
    枚 是任何类型 =;
}//都是类型
模块 用检查写;
导入 检查写;

空 主()
{
    c写格式行!"样例 #%d,的结果是 (%s, %f)"( 0, "福", 3.14); // 好

 // 差:坏数字/实参:期望3 实参,得到2个.
 // c写格式行!"样例#%d,的结果是 (%s, %f)"( 0, "福");

 // c写格式行!"样例 #%d,的结果是(%s, %f)"( 0, 3.14, "福");
//第3个应该为浮点.
}

给定格式串,先提取格式化器构造限制列表,这里用串插件,只需要构造表示期望的代码就行.

模块 取格式化器;
导入 标.转换;
导入 标.特征;

串 取格式化器(S)(S s)(是些串!S)
{
    d串 ds =!d串(s);%=;
    极 错误;
    串 结果 = "别名 元组类型!(";
    每一(元素; ds)
    {(错误);(%)
        {
            开关 (元素)
            {'%':
                    %=;;'d':'i':
                    结果 ~= "是完整,"; // 整数
                    %=;;'u':'x':'X':'o':
                    结果 ~= "是正,"; // 正整数
                    %=;;'f':'F':'e':'E':'g':'G':
                    结果 ~= "是浮点,"; // 浮点
                    %=;;'c':
                    结果 ~= "是些符,"; // 符
                    %=;;'s':
                    结果 ~= "是任何类型,";
                // 任何可转成串的类型
                    %=;;
                /* 标志, 宽度, */'+':'-':'#':'.':' ':'0':
                    ..'9':;
                默认:
                    错误 =; // 错误!;
            }
        }{(元素 == '%') %=;
        }
    }

    // 去掉尾逗号(结果.长度 > 17) 结果 = 结果[0..$-1];
    // 完成别名代码
    结果 ~= ") 检查实参;";(%// 完成串但仍在"%后" 模式
     || 错误)
        结果 = "静 断定(0, \"坏格式串: \" ~ a);";
    中 结果;
}

代码很长,但思路简单,迭代字符,查找%x模式,这里是提取格式化器当串错了,将生成静断.

// 无格式器
"别名 元组类型!() 检查实参;"

// 上个例子
"别名 元组类型!(是完整,是任何类型,是浮动类型) 检查实参;"

// 坏串
"静 断定(0, \"坏格式串: %s 和 %z\");"

然后需要用相应模板依次验证参数的模板,用整模板参数来取检查参数的个数.

模块 验证检查;
导入 标.转换;
导入 标.特征;
导入 标.类型元组;
导入 是任何类型;
导入 取格式化器;

元 检查实参(别名 a)(是些串!(的型(a)))
{
    插件(取格式化器(a));
}

元 验证检查(整 其, 检查...)//...,双层
{on(实参...)
    {
        静 如 (检查.长度 != 实参.长度)
            静 断定( 0
         , "编时实参数不对:期望"
         ~!(检查.长度)
         ~ "个实参,但得到"
         ~!(实参.长度)
         ~ ".");
        异 静 如 (检查.长度 == 0) //结束处理
            枚 on =;
        异 静 如 ({ 别名 检查[0] C; 中 C!(实参[0]);}()) // 递归,这一句核心.
            枚 on = 验证检查!(+1, 检查[1..$]).on!(实参[1..$]);
        异
            静 断定( 0, "c写坏参:#参"
         ~!()
         ~ " 类型 "
         ~ 实参[0].的串
         ~ "不满足"
         ~ __特征(标识符, 检查[0]));
    }
}
验证!(0, 是完整, 是浮点).on!(, 双精)

双层模板.D语言不允许异 静 如 (检查[0]!(实参[0])) // 递归,所以,这样

异 静 如 ({ 别名 检查[0] C; 中 C!(实参[0]);}()) // 递归
//标准方法
别名 检查[0] 检查;//然后
异 静 如 (检查!(实参[0]))

但这将在模板局部域中放一个符号,这会破坏同名技巧.因而定义一个用本地闭包验证实现.

{ 别名 检查[0] C; 中 C!(实参[0]);}()

大括号里面定义闭包,()调用它,返回真/假.然后插入静如.一旦检查完代码,就很容易了.

模块 检查写;
导入 标.标io;
导入 标.特征;
导入 验证检查;

空 c写f(别名 a, 实参...)(实参 实参)(是些串!(的型(a))
 && 验证检查!(1, 检查实参!(a)).on!(实参))
{f(a, 实参);
}

空 c写格式行(别名 a, 实参...)(实参 实参)(是些串!(的型(a))
 && 验证检查!(1, 检查实参!(a)).on!(实参))
{
    写格式行(a, 实参);
}

ucfs统调,自动将a.foo(b)转成foo(a,b).a不一定是类对象

插件 元 前向器()
{
    动 分发操作(串 名, 实参...)(实参 实参)
    {
        插件("中 " ~~ "(实参);");
    }
};

前向器,可用来分发外部自由函数.D中可返回.但跨模块边界时不能用.
聚集一堆模板,然后用他们的模板匹配能力.为元组生成开关.给定编译时信息,生成特定运行时代码.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值