【问题标题】:Memoize result of the same function called inside different delegates记忆在不同委托中调用的相同函数的结果
【发布时间】:2021-02-07 20:32:28
【问题描述】:

我有一个函数,它接受一个 Func 列表,每个都有一个附加值。如果委托返回 true,它会迭代列表并返回附加值。其中一些委托使用相同的参数调用相同的函数在 TryFindValue 方法的范围内如何最好地记住此类调用的结果?

函数的返回值在调用 TryFindValue 之间可能会发生变化,但在迭代列表时它们不会发生变化。我想避免产生垃圾(例如通过转换为对象来装箱/拆箱)。理想情况下,我只需要将空间用于 TryFindValue 范围内的记忆值,而不是例如每个委托的整个生命周期,但我不知道这是否可能。

public class SomeClass
{
    public bool TryFindValue(List<CondValue> list, out int value)
    {
        for (int i = 0; i < list.Count; i++)
        {
            var condValue = list[i];
            if (condValue.cond())
            {
                value = condValue.value;
                return true;
            }
        }
        value = default(int);
        return false;
    }
}

public class SomeOtherClass
{
    private List<CondValue> list;
    
    public SomeOtherClass()
    {
        list = new List<CondValue>();
        // SomeMethod(3) will be calculated twice
        list.Add(new CondValue(() => SomeMethod(3) > SomeOtherMethod(), 42));
        list.Add(new CondValue(() => SomeMethod(3) < SomeThirdMethod(), 35));
    }
    
    private float SomeMethod(int value)
    {
        // Implementation... (uses some internal or global state)
    }
    
    private int SomeOtherMethod()
    {
        // Implementation... (uses some internal or global state)
    }
    
    private int SomeThirdMethod()
    {
        // Implementation... (uses some internal or global state)
    }
}

public struct CondValue
{
    public Func<bool> cond;
    public int value;
    
    public CondValue(Func<bool> func, int value)
    {
        this.func = func;
        this.value = value;
    }
}

【问题讨论】:

  • 给委托添加上下文参数,带字典?

标签: c# delegates memoization


【解决方案1】:

您可以只返回值或空值,而不是让个别条件返回 true 或 false 并附加值。这有什么帮助?对于以下解决方案,我假设列表中调用同一函数的委托在同一函数中一起添加到列表中(此处在构造函数 SomeOtherClass 中)。从那里,您可以在 if-else if 代码块中将调用具有相同参数的相同函数的委托批处理在一起,并在委托顶部手动“记忆”函数的结果。

调用具有相同参数的相同函数的委托闻起来就像它们在这种 if-else if 关系中是相关的。我添加了 SomeFourthMethod 来展示如何仍然紧凑地单独添加单个条件。

如果您有改进此解决方案细节的建议(使用可以为空的其他内容等),请随时在评论中提出建议。

如果所有的条件总是独立的,只是碰巧有时调用了同一个函数,那么这个解决方案可能不合适。否则,这是我能想到的最简单最有效的解决方案。

public class SomeClass
{
    public bool TryFindValue(List<Func<int?>> list, out int value)
    {
        for (int i = 0; i < list.Count; i++)
        {
            var func = list[i];
            int? ret = func();
            if (ret.HasValue)
            {
                value = ret.Value;
                return true;
            }
        }
        value = default(int);
        return false;
    }
}

public class SomeOtherClass
{
    private List<Func<int?>> list;

    public SomeOtherClass()
    {
        list = new List<Func<int?>>();
        Add(() =>
        {
            float memoized = SomeMethod(3);
            if (memoized > SomeOtherMethod())
                return 42;
            else if (memoized < SomeThirdMethod())
                return 35;
            return null;
        });
        Add(() => SomeFourthMethod() > 4, 72);
    }

    private void Add(Func<int?> snippet)
    {
        list.Add(snippet);
    }

    private void Add(Func<bool> cond, int value)
    {
        list.Add(() =>
        {
            if (cond())
                return value;
            return null;
        });
    }

    private float SomeMethod(int value)
    {
        // Implementation... (uses some internal or global state)
    }

    private int SomeOtherMethod()
    {
        // Implementation... (uses some internal or global state)
    }

    private int SomeThirdMethod()
    {
        // Implementation... (uses some internal or global state)
    }

    private int SomeFourthMethod()
    {
        // Implementation... (uses some internal or global state)
    }
}

【讨论】:

    【解决方案2】:

    如果您将CondValue.cond 转换为表达式树而不是Func,那么也许您可以制定一些复杂的解决方案。我没有这样做的经验,我不建议冒险去冒险。除此之外,我没有看到一个好的解决方案,只在对TryFindValue 的一次调用的生命周期内将其缓存/记忆。

    我宁愿问你:

    • 您有当前设置有什么好的理由吗?
      • 这似乎是一个不必要的复杂设置。由于您只展示了一个非常抽象的示例,因此很难提出更好的替代方案。
    • 例如SomeMethod(3) 不总是返回相同的值?
      • 您可以将该方法包装在一些缓存逻辑中(例如,在 PostSharp 的帮助下)。但是,如果它不能始终返回相同的值,则必须在必要时清除该缓存,这使得它更难维护。

    【讨论】:

    • “函数的返回值可能会在调用 TryFindValue 之间发生变化”,为了示例,您应该假设 SomeMethod(3) 不一定在调用 TryFindValue 之间返回相同的值。跨度>
    • @John 我相信我给出了关于如何处理SomeMethod(3) 并不总是返回相同的情况的指示。你考虑过吗?至于跑题:我不认为建议你考虑你当前的设计是否合适是跑题的。 SO 上的很多人都遇到了XY Problem,因为他们问的是如何实施他们的解决方案,而不是问如何解决他们的问题。
    • 如果我知道 TryFindValue 中缓存值的类型,我可以传入在该范围内声明的 ref 变量。可以尝试通过低层次的思考来解决这个问题,例如delegate = 函数指针的种类以及我们可以使用引用和堆栈做什么。
    • @John 也许我以错误的方式阅读这些 cmets,但我感受到了很多敌意,所以我将把谈话留在这里。祝你项目好运。我真诚地希望您找到适合您的良好解决方案。
    • 对不起,我并不是故意表现出敌意。表达式树是一个很好的观点,我将尝试进一步探索。感谢您的帮助!
    猜你喜欢
    • 2019-01-06
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多