How can I make a variable always equal to the result of some calculations?

前端 未结 12 1721
傲寒
傲寒 2021-02-02 05:34

In math, if z = x + y / 2, then z will always change whenever we replace the value of x and y. Can we do that in programming

相关标签:
12条回答
  • 2021-02-02 06:01

    You can get close to this with by using a lambda in C++. Generally, when you set a variable like

    int x;
    int y;
    int z{x + y};
    

    z will only be the result of x + y at that time. You'd have to do z = x + y; every time you change x or y to keep it update.

    If you use a lambda though, you can have it capture what objects it should refer to, and what calculation should be done, and then every time you access the lambda it will give you the result at that point in time. That looks like

    int x;
    int y;
    auto z = [&](){ return x + y; };
    cin >> x;
    cin >> y;
    cout << z();
    

    and now z() will have the correct value instead of the uninitialized garbage that the original code had.

    If the computation is very expensive you can even add some caching to the lambda to make sure you aren't running the computation when you don't need to. That would look like

    auto z = [&](){ static auto cache_x = x; 
                    static auto cache_y = y; 
                    static auto cache_result = x + y;
                    if (x != cache_x || y != cache_y)
                    {
                        cache_x = x; 
                        cache_y = y; 
                        cache_result = x + y;
                    }
                    return cache_result;
    };
    
    0 讨论(0)
  • 2021-02-02 06:02

    There are two chief techniques:

    1. Deferred calculation - instead of z being a simple variable, make it a function which calculates the value on demand (see other answers for examples). This can be source-code transparent if z is some proxy object with implicit conversion to the required type (as in Aconcagua's answer).

    2. Explicit notification of changes. This requires x and y to be observable types; when either changes value, then z updates itself (and notifies its observers if applicable).

    The first version is usually preferred, but the second may be more appropriate if you need z to be an observable type.

    0 讨论(0)
  • 2021-02-02 06:02

    You can define the following lambda z which always returns the current value of x+y because x and y are captured by reference:

    DEMO

    int main()
    {
        int x;
        int y;
    
        const auto z = [&x, &y](){ return x+y; };
    
        std::cin  >> x; // 1
        std::cin  >> y; // 2
        std::cout << z() << std::endl; // 3
    
        std::cin  >> x; // 3
        std::cin  >> y; // 4
        std::cout << z() << std::endl; // 7
    }
    
    0 讨论(0)
  • 2021-02-02 06:02

    Ok, let me at last write the right and only true answer to your stated question:

    You can't.

    You can't write z = x + y and then have all the code using z magically re-run whenever x or y changes.

    So what can be done?

    As mentioned in other answers, there are several patterns to express that you want changes of x and y to cause some updates, but in any case you need these updates to happen more or less explicitly.

    Depending on a use case, you may:

    • Have the value recomputed anyway at all times this matters. E.g. if you write a game and redraw the screen every frame, then likely just making sure that you don't accidentally keep the z value between the frames is enough. Be aware of when your value can change and when it can't. Whether you use a function, a lambda, a class method, or just repeat the expression, is mostly esthetical decision. If available, this is the best approach, because it is fully transparent.

      For instance, in racing game you'd likely update your current speed at the beginning of the new tick computation, and then use the updated value when computing your car's movement, when redrawing the speed indicator, when creating the motion blur, and so on. You don't need any magic and not even a function, you can just use a variable, because you know your speed won't change during one frame.

    • Call the update explicitly. Use it e.g. when you have a single widget you need to update. Downside is that you need to remember to call the update, which is somewhat brittle, but on the upside - it is dead simple. A middle ground is to have the update call integrated with a setter, making it kind of poor man's Observer implementation.

    • Use Observer pattern (see also signals and slots, this is one way of implementing Observer). Use it e.g. when you have many widgets to update, or you create them dynamically. Avoid using it when one of the above works, they are way simpler.

    • Use dedicated reactive programming library. As such stuff exists, I feel obliged to mention it. However, I honestly don't see any application where I would use it. It mostly seems like a complicated way to shoot your feet. The implicit updates are going to backfire, and you'll have to rewrite everything. Just don't, not in C++. What is important: while this approach is closest to "magically update everything", it would impose constraints on how you write your code, and eventually you'll get one of the above solutions, just more complicated.

    0 讨论(0)
  • 2021-02-02 06:09

    You could write a class that encapsulates its state to update either when mutated or return the right result when requested :

    #include <iostream>
    
    template<typename T, typename U, typename V>
    class DynamicCalc
    {
    public:
        DynamicCalc(const T& func, const U& memberOne, const V& memberTwo) :
            _func(func)
          , _memberOne(memberOne)
          , _memberTwo(memberTwo)
        {
    
        }
    
        void SetMemberOne(const U& memberOne) { _memberOne = memberOne; }
        void SetMemberTwo(const U& memberTwo) { _memberTwo = memberTwo; }
        auto Retrieve() { return _func(_memberOne, _memberTwo); }
    
        U GetMemberOne() { return _memberOne; }
        V GetMemberTwo() { return _memberTwo; }
    
    private: 
        T _func;
    
        U _memberOne;
        V _memberTwo;
    };
    
    int main() {
    
        auto func = [](int x, int y) {
            return x + y;
        };
        DynamicCalc<decltype(func), int, int> c(func, 3, 5);
    
        c.SetMemberOne(5);
        std::cout << c.Retrieve();
    }
    

    In truth, if you're happy for the calculation to happen when the value is reuqested then the getters/setters are unnecessary.

    0 讨论(0)
  • 2021-02-02 06:10

    The closest you probably can get is to create a functor:

    #include <iostream>
    
    int main() {
        int x;
        int y;
    
        auto z = [&x, &y] { return x + y; }; // a lambda capturing x and y
    
        while(true) {
            std::cin >> x;
            std::cin >> y;
            std::cout << z() << "\n";
        }
    }
    
    0 讨论(0)
提交回复
热议问题