d按元素操作元组
原文
为何不按元素
操作元组?
假定,在Tuple!(int, int)
中存储2维点.直接a + b
就可产生另1个Tuple!(int, int)
.
直接,用c[] = a[] + b[]
的话,会动态分配
,并强制用户创建显式
的目的变量(c)
.
假定可直接如下,这就有点笨拙.
T opBinary(string op, T)(T lhs, T rhs)
if (isTuple!T)
{
T result;
static foreach (i; 0 .. T.Types.length)
{
mixin("result.field[i] = lhs.field[i]"~op~"rhs.field[i];");
}
return result;
}
只需要把它变成成员函数
就可了,甚至可使它更通用
,允许对不同但兼容
元组类型同样操作(有areCompatibleTuples
函数检查兼容性
).然而,只有opBinary
来特化连接元组
(及opCmp和opAssign
的实现).
我已编写了精确
实现这一点的Vec
类型,它在幕后使用元组
,并重载
操作符来允许向量算术
的良好语法.
/**
* 表示值的n维向量
*/
struct Vec(T, size_t n)
{
T[n] impl;
alias impl this;
/**
* 每元素的一元操作
*/
Vec opUnary(string op)()
if (is(typeof((T t) => mixin(op ~ "t"))))
{
Vec result;
foreach (i, ref x; result.impl)
x = mixin(op ~ "this[i]");
return result;
}
/**
* 每元素的二元操作
*/
Vec opBinary(string op, U)(Vec!(U,n) v)
if (is(typeof(mixin("T.init" ~ op ~ "U.init"))))
{
Vec result;
foreach (i, ref x; result.impl)
x = mixin("this[i]" ~ op ~ "v[i]");
return result;
}
/// 同上
Vec opBinary(string op, U)(U y)
if (isScalar!U &&
is(typeof(mixin("T.init" ~ op ~ "U.init"))))
{
Vec result;
foreach (i, ref x; result.impl)
x = mixin("this[i]" ~ op ~ "y");
return result;
}
/// 同上
Vec opBinaryRight(string op, U)(U y)
if (isScalar!U &&
is(typeof(mixin("U.init" ~ op ~ "T.init"))))
{
Vec result;
foreach (i, ref x; result.impl)
x = mixin("y" ~ op ~ "this[i]");
return result;
}
/**
* 每元素赋值操作符
*/
void opOpAssign(string op, U)(Vec!(U,n) v)
if (is(typeof({ T t; mixin("t " ~ op ~ "= U.init;"); })))
{
foreach (i, ref x; impl)
mixin("x " ~ op ~ "= v[i];");
}
void toString(W)(W sink) const
if (isOutputRange!(W, char))
{
import std.format : formattedWrite;
formattedWrite(sink, "(%-(%s,%))", impl[]);
}
}
/**
*创建向量的方便函数
*返回:Vec!(U,n),n=参数长度,U为参数公共类型,无公共类型,则编译时错误.
*/
auto vec(T...)(T args)
{
static if (args.length == 1 && is(T[0] == U[n], U, size_t n))
return Vec!(U, n)(args);
else static if (is(typeof([args]) : U[], U))
return Vec!(U, args.length)([ args ]);
else
static assert(false, "无公共类型" ~ T.stringof);
}
///
unittest
{
// 基本向量构建
auto v1 = vec(1,2,3);
static assert(is(typeof(v1) == Vec!(int,3)));
assert(v1[0] == 1 && v1[1] == 2 && v1[2] == 3);
// 向量比较
auto v2 = vec(1,2,3);
assert(v1 == v2);
// 一元运算
assert(-v1 == vec(-1, -2, -3));
assert(++v2 == vec(2,3,4));
assert(v2 == vec(2,3,4));
assert(v2-- == vec(2,3,4));
assert(v2 == vec(1,2,3));
// 二元向量运算
auto v3 = vec(2,3,1);
assert(v1 + v3 == vec(3,5,4));
auto v4 = vec(1.1, 2.2, 3.3);
static assert(is(typeof(v4) == Vec!(double,3)));
assert(v4 + v1 == vec(2.1, 4.2, 6.3));
// 标量的二元操作
assert(vec(1,2,3)*2 == vec(2,4,6));
assert(vec(4,2,6)/2 == vec(2,1,3));
assert(3*vec(1,2,3) == vec(3,6,9));
// 非数字向量
auto sv1 = vec("a", "b");
static assert(is(typeof(sv1) == Vec!(string,2)));
assert(sv1 ~ vec("c", "d") == vec("ac", "bd"));
assert(sv1 ~ "post" == vec("apost", "bpost"));
assert("pre" ~ sv1 == vec("prea", "preb"));
}
unittest
{
// 测试opOpAssign.
auto v = vec(1,2,3);
auto w = vec(4,5,6);
v += w;
assert(v == vec(5,7,9));
}
unittest
{
int[4] z = [ 1, 2, 3, 4 ];
auto v = vec(z);
static assert(is(typeof(v) == Vec!(int,4)));
assert(v == vec(1, 2, 3, 4));
}
unittest
{
import std.format : format;
auto v = vec(1,2,3,4);
assert(format("%s", v) == "(1,2,3,4)");
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?