0

I have a function that takes a list of Func<bool> each with an attached value. It iterates the list and returns the attached value if a delegate returns true. Some of these delegates call the same function with the same parameters how do I best memoize the result of such calls within the scope of the TryFindValue method?

The return value of the functions may change between calls to TryFindValue, but they will not change while iterating the list. I would like to avoid generating garbage (boxing/unboxing by casting to object for example). Ideally, I would only have to use the space for memoized values in the scope of TryFindValue and not for the entire lifetime of each delegate for example, but I do not know if that is possible.

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; } } 
1
  • Add a context parameter to the delegate, with a dictionary? Commented Oct 25, 2020 at 23:13

2 Answers 2

-1

If you turn CondValue.cond into an expression tree instead of a Func then maybe you could make some complicated solution. I have no experience doing so and I wouldn't suggest venturing out on that adventure. Apart from that I don't see a good solution to only keep it cached/memorized within the lifetime of a single call to TryFindValue.

I would rather ask you:

  • Is there any good reason you have the current setup?
    • It seems like an unnecessarily complicated setup. Since you've only shown a very abstract example it's difficult to suggest a better alternative.
  • Would e.g. SomeMethod(3) not always return the same value?
    • You could wrap that method in some caching logic (e.g. with help from PostSharp). But if it doesn't consistently return the same value, you'd have to clear that cache when necessary, which makes it harder to maintain.
Sign up to request clarification or add additional context in comments.

6 Comments

"The return value of the functions may change between calls to TryFindValue", you should assume SomeMethod(3) does not necessarily return the same value between calls to TryFindValue for the sake of the example.
@John I believe I gave a pointer on how to handle the situation where SomeMethod(3) does not always return the same. Have you considered that? As for being off-topic: I don't think it's off-topic to suggest you to think about whether your current design is appropriate. Plenty of people on SO run into the XY Problem because they ask how to implement their solution rather than asking how to solve their problem.
If I knew the type of the cached values in TryFindValue I could pass in ref variables declared in that scope. One could try to solve this problem by thinking low-level, e.g. delegate = kind of function pointer and what can we do using references and the stack.
@John Perhaps I'm reading these comments in the wrong way, but I'm sensing a lot of hostility, so I'll leave the conversation here. Good luck with you project. I do sincerely hope you find a good solution that works for you.
Sorry I did not mean to seem hostile. The expression tree is a good point I will try and explore that further. Thanks for the help!
|
-1

Instead of having individual conditions that return true or false with an attached value you could just return the value or null. How does that help? For the following solution, I am assuming that the delegates in the list that call the same function are added to the list together in the same function (here in the constructor SomeOtherClass). From there you could batch together delegates that call the same functions with the same parameters in an if-else if code block and manually "memoize" the result of the function at the top of the delegate.

Delegates that call the same functions with the same parameters smells like they are related in this if-else if relationship anyway. I have added SomeFourthMethod to show how to still compactly add a single conditional individually.

If you have suggestions to improve the details of this solution (using something else than nullable, etc.) then feel free to suggest it in a comment.

If all the conditions are always independent and just happen to sometimes call the same function, then this solution might not be appropriate. Otherwise, it is the simplest most effective solution I can think of.

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) } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.