假设我有一个enum Direction
,它指定了基数(和心轴间)方向:
public enum Direction
{
None = 0,
Up = 1 << 1,
Down = 1 << 2,
Left = 1 << 3,
Right = 1 << 4,
UpLeft = Up | Left,
UpRight = Up | Right,
DownLeft = Down | Left,
DownRight = Down | Right
}
在不引入冲突方向(向上和向下)的情况下,为了反转任意方向,我可以执行的最短逐位运算是什么?
我知道我可以使用三级语句来有效地处理这个问题
public Direction Inverse (Direction d) {
// Inverse of Direction.None should still be none
if (d == Direction.None) { return d; }
Direction dInvert = (d.HasFlag(Direction. Up)) ? Direction.Down : Direction.Up;
dInvert |= (d.HasFlag(Direction.Left)) ? Direction.Right : Direction.Left;
return dInvert;
}
但我宁愿学习如何使用二元运算符处理这种“对称枚举反转”。如果没有代码打高尔夫,我能想到的最好的是
dir ^ (Direction.Up | Direction.Down)
但这显然只有在dir
不包含任何水平“组件”时才有效。
枚举应该看起来像这样(从1 << 0 == 1
开始减少一个):
public enum Direction
{
None = 0,
Up = 1 << 0,
Down = 1 << 1,
Left = 1 << 2,
Right = 1 << 3,
UpLeft = Up | Left,
UpRight = Up | Right,
DownLeft = Down | Left,
DownRight = Down | Right
}
让我们分析不同的可能性。这是一个带有标志和相应反转的表(只有最后8位)
+-----------+----------------+----------------+
| Direction | Flags | Inverted |
+---------------------------------------------+
| None | 0000 0000 = 0 | 0000 0000 = 0 |
| Up | 0000 0001 = 1 | 0000 0010 = 2 |
| Down | 0000 0010 = 2 | 0000 0001 = 1 |
| Left | 0000 0100 = 4 | 0000 1000 = 8 |
| Right | 0000 1000 = 8 | 0000 0100 = 4 |
| UpLeft | 0000 0101 = 5 | 0000 1010 = 10 |
| UpRight | 0000 1001 = 9 | 0000 0110 = 6 |
| DownLeft | 0000 0110 = 6 | 0000 1001 = 9 |
| DownRight | 0000 1010 = 10 | 0000 0101 = 5 |
+-----------+----------------+----------------+
组合方向可以用~dir & 0b1111
反转,但我没有看到简单方向的简单按位操作。
让我们看一下十进制值。我们可以尝试推断一个函数。
inv.
^
10 | X
9 | X
8 | X
7 |
6 | X
5 | X
4 | X
3 |
2 | X
1 | X
0 X--+--+--+--+--+--+--+--+--+--+--> flag
0 1 2 3 4 5 6 7 8 9 10
这看起来也不简单。
WolframAlpha可以从这些数字对中插入多项式
结果是:
- (x(x7 - 40 x6 + 686 x5 - 6580 x4 + 37709 x3 - 124180 x2 + 203524 x - 131280))/ 10080
uugh!
我会做一个反转字典
var inversion = new Dictionary<Direction, Direction> {
[Direction.None] = Direction.None,
[Direction.Up] = Direction.Down,
[Direction.Down] = Direction.Up,
[Direction.Left] = Direction.Right,
[Direction.Right] = Direction.Left,
[Direction.UpLeft] = Direction.DownRight,
[Direction.UpRight] = Direction.DownLeft,
[Direction.DownLeft] = Direction.UpRight,
[Direction.DownRight] = Direction.UpLeft
}
并获得倒转的方向
Direction invDir = inversion[dir];
最后,我找到了一个使用负枚举值的简单解决方案:
如果你这样声明枚举:
public enum Direction
{
None = 0,
Up = 1,
Down = -1,
Left = 4,
Right = -4,
UpLeft = Up + Left,
UpRight = Up + Right,
DownLeft = Down + Left,
DownRight = Down + Right
}
您可以通过使用二进制补码取负值来获得反转方向
~d + 1
foreach (Direction d in Enum.GetValues(typeof(Direction))) {
Console.WriteLine($"{d} ===> {~d + 1}");
}
打印
无===>无 上来===>下来 DownLeft ===> UpRight 左===>对 UpLeft ===> DownRight DownRight ===> UpLeft 对===>左 UpRight ===> DownLeft 下来===> Up
简单地使用-d
不起作用,因为-
不能应用于枚举。你必须写(Direction)(-((int)dir))
。
我用两对比特编码方向,一对用于垂直,一对用于水平:
enum Direction
{
None = 0b00_00,
Down = 0b00_01,
Up = 0b00_10,
Right = 0b01_00,
DownRight = 0b01_01,
UpRight = 0b01_10,
Left = 0b10_00,
DownLeft = 0b10_01,
UpLeft = 0b10_10
}
static Direction Inverse(Direction dir)
{
int d = (int)dir;
Direction ret = (Direction)(((d & 0b01_01) << 1) |
((d & 0b10_10) >> 1));
return ret;
}
static bool HasDir(Direction dir, Direction query)
{
return query == (Direction)((int)query & (int)dir);
}
static bool HasUp(Direction dir)
{
return HasDir(dir, Direction.Up);
}
static bool HasDown(Direction dir)
{
return HasDir(dir, Direction.Down);
}
static bool HasRight(Direction dir)
{
return HasDir(dir, Direction.Right);
}
static bool HasLeft(Direction dir)
{
return HasDir(dir, Direction.Left);
}
private static string show(Direction dir)
{
return (HasDown(dir) ? "V" : HasUp(dir) ? "^" : "") +
(HasLeft(dir) ? "<" : HasRight(dir) ? ">" : "");
}
static void Main(string[] args)
{
foreach(Direction dir in Enum.GetValues(typeof(Direction)))
{
Console.WriteLine(show(dir) + " " + show(Inverse(dir)));
}
Console.WriteLine("ciao!");
}
Inverse
操作只需交换两对。
juharr对你的问题提出了评论,这与我的想法一致。即:您确定将方向表示为四个两位值的组合是否有意义?如您所述,许多可能的组合都是无效的,因为您不能在相同的值中使用相反的方向。相反,两个三位值的组合可能更合适:一个用于表示水平方向,另一个用于表示垂直方向。
这是一个可能是什么样子的例子。 Direction
是一个提供产生水平和垂直分量的属性的结构,以及一个简单的反演方法。
public enum HorizontalDirection
{
Left = -1,
None = 0,
Right = 1,
}
public enum VerticalDirection
{
Up = -1,
None = 0,
Down = 1,
}
public struct Direction
{
public Direction(HorizontalDirection h) : this(h, VerticalDirection.None) { }
public Direction(VerticalDirection v) : this(HorizontalDirection.None, v) { }
public Direction(HorizontalDirection h, VerticalDirection v)
{
HorizontalComponent = h;
VerticalComponent = v;
}
public HorizontalDirection HorizontalComponent { get; }
public VerticalDirection VerticalComponent { get; }
public Direction Invert() => new Direction(
(HorizontalDirection)(-(int)HorizontalComponent),
(VerticalDirection)(-(int)VerticalComponent));
}