C#Basic堆栈算术运算

时间:2015-08-29 20:38:59

标签: c# math stack push pop

我有两个类,一个是Calculator,另一个是Test类。 Test类包含多个预期输出测试和计算器包含执行算术运算的方法任何有助于

的教程

3 个答案:

答案 0 :(得分:4)

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

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操作从堆栈顶部提取上次推送的值(ed),此值为当前总计,您有兴趣恢复之前的值,因此丢弃实际顶部并分配总计新的堆栈顶部

修改
在考虑了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;
    }
}

这里主要的变化是存储在堆栈中的内部类CalcAction,其中包含当前操作的信息。这允许RepeatLastAction方法知道要重新执行哪个操作。唯一的例外是从堆栈中保留的撤销操作,因为它的签名与Add / Subtract / Multiply不同

答案 1 :(得分:1)

使用此代码

添加operationInfo,

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 五 2 0

答案 2 :(得分:0)

这堂课有很多错误。

(1)Add正在推动,而其他操作正在推动总计
(2)如果您需要撤销,所有操作都应该在修改之前按总计 (3)撤消应该只是弹出一个 (4)RepeatLastCommand没有意义,因为你只将结果存储在堆栈中,而不是操作。

修改
在上一部分中,我试图回答您的原始问题 - 为什么您获得了这样的结果。但如果问题是如何使它工作,这里IMO是最简单的方法。如上所述,为了支持Undo,我们所需要的只是将之前的值存储在堆栈中。为了支持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#closure将为我们做所有这些,并且还允许我们抽象操作并支持二元,一元或没有arg操作。出于演示目的,我添加了NegateDivide操作,以及使Value设置操作无法撤销(如果您不需要,可以删除它)。