给定方向枚举,最小的逐位反转方法是什么?

Enf*_*yve 1 c# enums

假设我有一个枚举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
}
Run Code Online (Sandbox Code Playgroud)

在不引入冲突方向(向上和向下)的情况下,为了反转任意方向,我可以执行的最短逐位运算是什么?

我知道我可以使用三级语句来有效地处理这个问题

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;
}
Run Code Online (Sandbox Code Playgroud)

但我宁愿学习如何使用二元运算符处理这种"对称枚举反转".如果没有代码打高尔夫,我能想到的最好的是

dir ^ (Direction.Up | Direction.Down)
Run Code Online (Sandbox Code Playgroud)

但这显然只有在dir不包含任何水平'组件' 时才有效.

Oli*_*bes 7

枚举应该看起来像这样(因此减少了一个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
}
Run Code Online (Sandbox Code Playgroud)

让我们分析不同的可能性.这是一个带有标志和相应反转的表(只有最后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 |
+-----------+----------------+----------------+
Run Code Online (Sandbox Code Playgroud)

组合方向可以反转~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
Run Code Online (Sandbox Code Playgroud)

这看起来也不简单.

WolframAlpha可以从这些数字对中插入多项式

InterpolatingPolynomial [{{0,0},{1,2},{2,1},{4,8},{5,10},{6,9},{8,4},{9,6} ,{10,5}},x]

结果是:

- (X(X ^ 7 - 40×^ 6 + 686的x ^ 5 - 6580的x ^ 4 + 37709的x ^ 3 - 124180的x ^ 2 + 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
}
Run Code Online (Sandbox Code Playgroud)

并获得倒转的方向

Direction invDir = inversion[dir];
Run Code Online (Sandbox Code Playgroud)

最后,我发现了一个简单的解决方案:

如果你这样声明枚举:

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
}
Run Code Online (Sandbox Code Playgroud)

您可以通过使用二进制补码取负值来获得反转方向
~d + 1

foreach (Direction d in Enum.GetValues(typeof(Direction))) {
    Console.WriteLine($"{d}  ===> {~d + 1}");
}
Run Code Online (Sandbox Code Playgroud)

打印

无===>无
Up ===> Down
DownLeft ===> UpRight
Left ===> Right
UpLeft ===> DownRight
DownRight ===> UpLeft
Right ===> Left
UpRight ===> DownLeft
Down = ==> Up

简单地使用-d不起作用,因为-无法应用于枚举.你必须写(Direction)(-((int)dir)).