d标准库选讲
作者:阿里
思想:AA:通过组合特征,来完成功能.
内容:涉及iota,parallel,static if,std.concurrency.receive和SumType.
iota
auto iota(B, E)(B begin, E end)
if (!isIntegral!(CommonType!(B, E)) &&
!isFloatingPoint!(CommonType!(B, E)) &&
!isPointer!(CommonType!(B, E)) &&
is(typeof((ref B b) { ++b; })) &&
(is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) )
{
// ...
}
D语言:更简单,更安全,更正确,更快,节省时间.
从dmd2.100.0开始,有54个标准模块.
//iota
auto iota(B, E, S)(B begin, E end, S step)
if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
&& isIntegral!S)
{
// ...
void popFront()
{
assert(!empty);
if (current == last) step = 0;
else current += step;
}
// ...
}
标准库用区间来抽象.
iota(0,10,2)//生成0,2,4,6,和8
简单iota示例:
struct MyNumbers {
int begin;// 仅同整,可模板化
int end;
int step;
bool empty() {
return begin >= end;
}
int front() {
return begin;
}
void popFront() {
begin += step;// 对浮点,这不准确.
}
}
辅助函数:
MyNumbers myNumbers(int begin, int end, int step) {
return MyNumbers(begin, end, step);
}
//单元测试
unittest {
assert(myNumbers(0, 10, 2).equal([0, 2, 4, 6, 8]));
}
移动构至函数内,变成隐藏类型:
auto myNumbers(int begin, int end, int step) {
struct MyNumbers {
// 这次,无数字(成员),用参数.
bool empty() {return begin >= end;}
int front() {return begin;}
void popFront() {begin += step;}
}
return MyNumbers();
}
免责声明:这不必要地昂贵,因为是闭包,动态分配的上下文会始终保持活动状态.可这样:
auto myNumbers(int begin, int end, int step) {
static struct MyNumbers {
// ...
}//静态构.
return MyNumbers(begin, end, step);
}
auto返回类型,表示自动推导返回类型.
auto iota(B, E, S)(B begin, E end, S step)
if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))&& isIntegral!S)
{ /* ... */ }
B,E,S表示类型.
模板约束:B和E是整数或指针且S是整数时使用.
其余定义:
//无步.
auto iota(B, E)(B begin, E end)
if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
{
return iota(begin, end, CommonType!(B, E)(1));
}
//无头参数
auto iota(E)(E end)
if (is(typeof(iota(E(0), end))))
{
E begin = E(0);
return iota(begin, end);
}
//浮点版
auto iota(B, E, S)(B begin, E end, S step)
if (isFloatingPoint!(CommonType!(B, E, S)))
{
// ...
Value front() const { assert(!empty); return start + step * index; }
void popFront()
{
assert(!empty);
++index;//start+=step对浮点没用.
}
// ...
}
//无步浮点.
auto iota(B, E)(B begin, E end)
if (isFloatingPoint!(CommonType!(B, E)))
{ /* ... */ }
用户定义的特化的一站式iota:
auto iota(B, E)(B begin, E end)
if (!isIntegral!(CommonType!(B, E)) &&
!isFloatingPoint!(CommonType!(B, E)) &&
!isPointer!(CommonType!(B, E)) &&
is(typeof((ref B b) { ++b; })) &&
(is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) )
{ /* ... */ }
CommonType为公共类型.即A,B都可转或三元运算符将选择的类型.如:
static assert(is (CommonType!(double, int, short) == double));
is(typeof(expr)),is和typeof都是编译时求值.
typeof(expr):表达式的类型.
is(Type):如果Type在语义上是正确的,则为true.示例:
//如下`is`式为假.
is( // 3) 假
typeof( // 2) λ没有类型
(string s) {
++s; // 1) 非法操作串.
}
)
等价于:
//`__traits`式为假.
__traits(compiles,(string s) { ++s; })
Unqual
去限定,这是头可变,
推导模板类型时保留了限定符:
void main() {
const a = 42;
foo(a);
}
void foo(A)(A a) {
A result; // A推导为const(int),
++result; // 因而,编译错误!
}
//改为如下
void foo(A)(A a) {
Unqual!A result;// 'result'是'int'
++result; // 现在编译了.
}
并行
在所有cpu中并行处理元素.
Student[] students;
// ...
foreach (s; students.parallel) {
// ...
}
//等价:
foreach (s; parallel(students)) {
// ...
}
调度到全局区间对象成员函数的函数:
ParallelForeach!R parallel(R)(R range)
{
return taskPool.parallel(range);
}
//带可选括号的等价物:
ParallelForeach!R parallel(R)(R range)
{
return taskPool().parallel(range);
}
延迟初化的全局对象:
@property TaskPool taskPool() @trusted
{
import std.concurrency : initOnce;
__gshared TaskPool pool;
return initOnce!pool({
auto p = new TaskPool(defaultPoolThreads);
p.isDaemon = true;
return p;
}());
}
initOnce用使用了互斥锁的initOnceLock.
TaskPool.parallel返回ParallelForeach对象:
final class TaskPool
{
// ...
ParallelForeach!R parallel(R)(R range)
{
// ...
}
}
ParallelForeach通过一对opApply函数支持foreach迭代:
private struct ParallelForeach(R)
{
// ...
int opApply(scope NoIndexDg dg)
{
static if (randLen!R) {
mixin(parallelApplyMixinRandomAccess);
} else {
mixin(parallelApplyMixinInputRange);
}
}
int opApply(scope IndexDg dg) { /* ... */ }
}
实现,在串插件中:
private enum string parallelApplyMixinRandomAccess = q{
// ...
// 迭代是否有索引.
enum withIndex = Parameters!(typeof(dg)).length == 2;
// ...
void doIt()
{
// ...
}
submitAndExecute(pool, &doIt);
return 0;
};
通过,UFCS/可选括号/互斥锁保护的延迟初化/opApply支持的每一/自省/串插件来实现.
内省设计的力量
auto r = iota(100)
.map!(n => n * n)
.stride(2) //也可用iota
.take(5);
writeln(r);//[0, 4, 16, 36, 64]
writeln(r[2]);//这样呢?
//也可以.
因为,take/stride/map/iota都支持.
D拥有static if.
struct Take(Range)
// ...
{
// ...
static if (isRandomAccessRange!R)
{
// ...
auto ref opIndex(size_t index)
{
assert(index < length,"越界"~ Take.stringof);
return source[index];
}
// ...
}
}
匹配模式
D无此语言功能.但可模拟它.
如,std.concurrency.receive可按消息类型分派不同闭包.
receive(
(LinkTerminated msg) {
// 终止消息
// ...
},
(Result result) {
// 发送结果,
// ...
},
(Foo foo, Bar bar) {
// 发送foo及bar
// ...
},
);
std.concurrency用Variant发送各种类型消息:
struct Message
{
MsgType type;
Variant data;
// ...
}
运行时线性搜索来匹配模式.ops为操作数组,Ops为类型元组.
foreach (i, t; Ops)
{
alias Args = Parameters!(t);
auto op = ops[i];
// ...
if (msg.convertsTo!(Args))// 用Variant.convertsTo
{
// 找到匹配
// ... 调用'op'并返回 ...
}
}
assert和static assert可提供有用的错误消息
如,在std.concurrency.MessageBox.get内部:
class MessageBox
{
// ...
bool get(T...)(scope T vals)
{
// ...
static assert(T.length, "T不能为空");
// ...
}
// ...
}
更多:
static assert(a1.length != 1 || !is(a1[0] == Variant),"带参函数" ~ a1.stringof ~
"不能为连续函数");
Variant,类似C++的any,不推荐Algebraic.用SumType.
SumType
序号 | 特点 |
|---|---|
1 | 匹配模式 |
2 | 支持自引用类型 |
3 | 完整正确的推导属性,尽可能推导pure,@safe,@nogc和nothrow. |
4 | 与DIP1000(域)兼容的类型安全和内存安全API. |
5 | 不依赖运行时类型信息 |
6 | 兼容BetterC |
示例:
//定义
struct Fahrenheit { double degrees; }
struct Celsius { double degrees; }
struct Kelvin { double degrees; }
alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
//构建
Temperature t1 = Fahrenheit(98.6);
Temperature t2 = Celsius(100);
Temperature t3 = Kelvin(273);
匹配模式:
Fahrenheit toFahrenheit(Temperature t) {
return Fahrenheit(
t.match!(
(Fahrenheit f) => f.degrees,
(Celsius c) => c.degrees * 9.0/5 + 32,
(Kelvin k) => k.degrees * 9.0/5 - 459.4
)
);
}
多重调度:
match!(// 4种处理器,处理3x3==9种情况
(Fahrenheit f1, Fahrenheit f2) => writeln("Both F"),
(Celsius c1, Celsius c2) => writeln("Both C"),
(Kelvin k1, Kelvin k2) => writeln("Both K"),
(_1, _2) => writeln("Different"),
)(t1, t2);
编译时的SumType
构建查找处理器:
private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
// ...
enum matches = ()
{
size_t[numCases] matches;
// ...
static foreach (caseId; 0 .. numCases)
{
static foreach (hid, handler; handlers)
{
static if (canMatch!(handler, valueTypes!caseId))
{
// ...
matches[caseId] = hid;
// ...
}
}
}
return matches;
}();
构建处理程序名:
enum handlerName(size_t hid) = "handler" ~ toCtString!hid;
static foreach (size_t hid, handler; handlers)
{
mixin("alias ", handlerName!hid, " = handler;");
}
编译时构建switch语句:
immutable argsId = TagTuple(args).toCaseId;
final switch (argsId)
{
static foreach (caseId; 0 .. numCases)
{
case caseId:
static if (matches[caseId] != noMatch)
{
return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")");
}
else
{
static if (exhaustive)
{
static assert(false,
"No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
}
else
{
throw new MatchException(
"No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
}
}
}
}
assert(false, "unreachable");
}
SumType支持递归数据类型.
alias Expr = SumType!(
double,
string,
Tuple!(Op,"op",This*,"lhs",This*,"rhs")
);
// ...
struct This {}
浙公网安备 33010602011771号