Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C基本堆栈算术运算_C#_Math_Stack - Fatal编程技术网

C# C基本堆栈算术运算

C# C基本堆栈算术运算,c#,math,stack,C#,Math,Stack,我有两门课,一门是计算器课,另一门是测试课。Test类包含多个预期输出Test和calculator包含执行算术运算的方法我的问题是:为什么TestOne输出105而不是9,TestTwo输出6而不是2 以下是测试 public void TestOne() { var c= new Calc(); c.Add(5); c.Add(7); c.Undo(); c.Subtract(2); c.Multiply(7); c.Undo();

我有两门课,一门是计算器课,另一门是测试课。Test类包含多个预期输出Test和calculator包含执行算术运算的方法我的问题是:为什么TestOne输出105而不是9,TestTwo输出6而不是2

以下是测试

public void TestOne()
{
    var c= new Calc();
    c.Add(5);
    c.Add(7);
    c.Undo();
    c.Subtract(2);
    c.Multiply(7);
    c.Undo();
    c.Multiply(3);
}

public void TestTwo()
{
    var c= new Calc();
    c.Add(2);
    c.Add(3);
    c.Add(4);
    ex.Undo();
    c.RepeatLastCommand();
}
下面是应用程序的功能

public class Calc
{
    int total= 0;
    Stack<int> stack = new Stack<int>();

    public int Value
    {
        get { return total; }
        set { total= value; }
    }

    public void Add(int value)
    { 
        total = total + value;
        stack.Push(value); 
    }

    public void Subtract(int value)
    {
        total = total - value;
        stack.Push(total);
    }

    public void Multiply(int value)
    {
        total= total * value;
        stack.Push(total);
    }

    public void RepeatLastCommand()
    {
        int topOfStack =stack.Peek();
        total += topOfStack;
     }

     public void Undo()
     {
         total = stack.Pop();
         if (stack.Count > 0)
         {
             int safe = stack.Pop();
          }
     }
}

乍一看,您需要将撤消方法更改为

public void Undo()
{
    if(stack.Count > 0 ) stack.Pop();
    total = stack.Peek();
}
此外,Add方法应该将Total变量而不是传递的值推送到堆栈上

public void Add(int value)
{
    total = total + value;
    stack.Push(total);
}
但是,解析RepeatLastCommand操作要复杂一点,并且无法使用此结构实现,您还需要将执行的操作和该操作中使用的值存储在堆栈变量中

我认为csdp000在另一个答案中提出的解决方案可以让您走上正确的道路

相反,关于您澄清撤销中的更改的请求,很简单。Pop操作从堆栈顶部提取上次推送的值,该值为当前总计,您有兴趣恢复以前的值,因此放弃实际的顶部并将新的堆栈顶部分配给总计

编辑 在考虑了RepeatLastAction之后,我重写了您的类,以存储一个包含Calc类执行的操作的所有信息的类实例,而不是整数

public class Calc
{
    bool undoAction = false;
    int total = 0;
    Stack<CalcAction> stack = new Stack<CalcAction>();

    public int Value
    {
        get
        {
            return total;
        }
        set
        {
            total = value;
        }
    }

    public void Add(int value)
    {
        CalcAction act = new CalcAction() 
        { 
            operation = Add, 
            actionTotal = total + value, 
            actionValue = value 
        };
        total = act.actionTotal;
        stack.Push(act);
        undoAction = false;
    }

    public void Subtract(int value)
    {
        CalcAction act = new CalcAction()
        {
            operation = Subtract,
            actionTotal = total - value,
            actionValue = value
        };
        total = act.actionTotal;
        stack.Push(act);
        undoAction = false;
    }

    public void Multiply(int value)
    {
        CalcAction act = new CalcAction()
        {
            operation = Multiply,
            actionTotal = total * value,
            actionValue = value
        };
        total = act.actionTotal;
        stack.Push(act);
        undoAction = false;
    }

    public void RepeatLastCommand()
    {
        if (stack.Count > 0)
        {
            if(undoAction)
                Undo();
            else
            {
                CalcAction act = stack.Peek();
                act.operation(act.actionValue);
            }
        }
    }

    public void Undo()
    {
        if (stack.Count > 0) stack.Pop();
        total = ((CalcAction)stack.Peek()).actionTotal;
        undoAction = true;
    }

    internal class CalcAction
    {
        public Action<int> operation;
        public int actionValue;
        public int actionTotal;
    }
}
这里的主要变化是内部类calAction,它与当前操作的信息一起存储在堆栈中。这允许RepeatLastAction方法知道要重新执行的操作。唯一的例外是撤销操作,由于其签名不同于加法/减法/乘法,因此它被排除在堆栈之外。请使用此代码

添加操作信息

public class CalcFix
{
    struct OperStruct
    {
        public Action<int> OperFunc;
        public int OperValue;
        public static OperStruct OperSet(Action<int> _operFunc, int _operValue)
        {
            OperStruct operStru = new OperStruct();

            operStru.OperFunc = _operFunc;
            operStru.OperValue = _operValue;
            return operStru;
        }

    }

    private bool _undo = false;
    private int _total = 0;
    private Dictionary<Action<int>, Action<int>> _reverseOper = new Dictionary<Action<int>, Action<int>>();
    private Stack<OperStruct> stack = new Stack<OperStruct>();

    public CalcFix()
    {
        _reverseOper.Add(Add, Subtract);
        _reverseOper.Add(Subtract, Add);
        _reverseOper.Add(Multiply, Division);
        _reverseOper.Add(Division, Multiply);
    }


    public int Value { get { return _total; } }

    public void Add(int value)
    {
        _total += value;
        _undo = false;
        stack.Push(OperStruct.OperSet(Add, value));
    }
    public void Subtract(int value)
    {
        _total -= value;
        _undo = false;
        stack.Push(OperStruct.OperSet(Subtract, value));
    }
    public void Multiply(int value)
    {
        _total *= value;
        _undo = false;
        stack.Push(OperStruct.OperSet(Multiply, value));
    }
    public void Division(int value)
    {
        _total /= value;
        _undo = false;
        stack.Push(OperStruct.OperSet(Division, value));
    }

    public void Undo()
    {
        OperStruct operFunc = stack.Pop();
        if (operFunc.OperFunc != null && _reverseOper.ContainsKey(operFunc.OperFunc))
        { 
            _reverseOper[operFunc.OperFunc](operFunc.OperValue);
            _undo = true;
        }
    }

    public void RepeatLastCommand()
    {
        OperStruct topOfStack = stack.Peek();
        if (stack.Count > 1)
        {
            if (topOfStack.OperFunc != null)
            {
                if (!_undo) // if not called undo
                    _reverseOper[topOfStack.OperFunc](topOfStack.OperValue);
                else // called undo
                {
                    stack.Pop();
                    topOfStack = stack.Pop();
                    _reverseOper[topOfStack.OperFunc](topOfStack.OperValue);
                    _undo = true;
                }
            }
        }
    }
}

public static void TestTwo()
{
    var c = new CalcFix();
    c.Add(2);
    c.Add(3);
    c.Add(4);
    System.Console.WriteLine(c.Value);
    c.Undo();
    System.Console.WriteLine(c.Value);
    c.RepeatLastCommand();
    System.Console.WriteLine(c.Value);
    c.RepeatLastCommand();
    System.Console.WriteLine(c.Value);
}
static void Main(string[] args)
{ 
    TestTwo();
}
更改代码

输出: 9 5. 2.
0

此类中有许多错误

1 Add是推送值,而其他操作是推送总计 2如果需要撤消,则所有操作都应在修改之前推送总计。 3“撤消”只需执行一次弹出操作。 4 RepeatLastCommand没有意义,因为您只在堆栈中存储结果,而不存储操作

编辑 在上一部分中,我试图回答你最初的问题——为什么你会得到这样的结果。但是,如果问题是如何使它工作,这里是最简单的方法。如上所述,为了支持撤销,我们只需要在堆栈中存储上一个值。为了支持RepeatLastCommand,我们只需要存储最后一个命令:-

以下是一种可能的实现方式:

public class Calc
{
    int total = 0;
    Stack<int> stack = new Stack<int>();
    Action lastCommand;

    public int Value
    {
        get { return total; }
        set { Execute(() => total = value); }
    }

    public void Add(int value)
    {
        Execute(() => total += value);
    }

    public void Subtract(int value)
    {
        Execute(() => total -= value);
    }

    public void Multiply(int value)
    {
        Execute(() => total *= value);
    }

    public void Divide(int value)
    {
        Execute(() => total /= value);
    }

    public void Negate()
    {
        Execute(() => total = -total);
    }

    public void RepeatLastCommand()
    {
        if (lastCommand != null)
            lastCommand();
    }

    public void Undo()
    {
        Execute(() => { if (stack.Count > 0) total = stack.Pop(); }, undoable: false);
    }

    private void Execute(Action command, bool undoable = true)
    {
        var oldValue = total;
        command();
        lastCommand = command;
        if (undoable) stack.Push(oldValue);
    }
}

请注意,我们不需要像其他答案中那样的特殊标志或类,因为C闭包将为我们完成所有这些,并且还允许我们抽象操作并支持二进制、一元或无arg操作。出于演示目的,我添加了求反和除法操作,以及使值集操作可撤消。如果不需要,您可以将其删除。

当您在调试器中逐步执行此操作时,我们不会为您执行此操作,具体来说,逻辑在哪里偏离了您的预期?我能想到的唯一问题是我正在应用的逻辑来完成操作。我对这个领域基本上是新手,所以我希望有人能在几个小时的努力后发现一个错误。我真的不确定撤销方法在做什么,真的。Undo和RepeatLastCommand似乎都不支持除了加法之外的任何操作,我甚至不认为Undo正确地支持加法。最好的方法是在调试器中单步执行,并跟踪每行代码后运行的值。这应该会告诉你一个值在什么地方发生了意外的变化。@David我是Git上的克隆src,但我正在尝试,我对这一点还是比较陌生的。例如,在TestTwo上重复。如果执行了Repeat,它应该执行undo,但我还没来得及调用它。他应该使用命令模式,但可能有点太高级了。你好,史蒂夫。从这个小小的变化来看,它似乎已经修复了TestOne,但TestOne仍然是一个问题。但是System.InvalidOpertation:Stack empty触发了谢谢,您能解释一下区别吗。您希望RepeatLastCommand会发生什么?这似乎无法用这种结构解决,您还需要存储运算符,在撤消的情况下,我的意思是,如果在撤消后调用RepeatLastCommand,您希望执行撤消,而如果在加法/减法/除法/乘法后使用RepeatLastCommand,您希望再次执行该命令,但此时,您还需要存储最后一个操作以及传递给该操作的参数。仅仅一个堆栈是不够的。可以存储操作吗?我来看看这个。TestOne正在抛出10你知道为什么吗?同意RepeatLastCommand。我确实一步一步地完成了它,当它在第二个测试中遇到这个问题时,它没有撤消Add3或撤消,而是实际上撤消了一个
Add5是Add2和Add3的总和。恐怕需要相当多的编码才能使它正常工作。这似乎与我试图编写以解决RepeatLastCommand问题的代码非常相似。好的工作唯一的问题似乎是如果RepeatLastCommand跟随一个加法/减法或乘法。它将恢复为撤消,而不是重复加法/减法运算/Multiply@Steve谢谢,你的代码非常干净。你对如何解决这个问题有什么建议吗?在我的代码中,我将撤销操作保留在堆栈之外,并设置一个标志来处理这种特殊情况。可能它适用于您的代码,但更优雅的解决方案当然是更好的尝试,但通常不起作用。零乘和7/3等整数除法不能以这种方式撤消。