【问题标题】:How to write a recursive function in C# that looks like A(key, B(key, C(key, ValFactory(key))))?如何在 C# 中编写一个看起来像 A(key, B(key, C(key, ValFactory(key)))) 的递归函数?
【发布时间】:2012-12-08 15:02:48
【问题描述】:

如何编写具有这种形式的函数:

A(key, B(key, C(key, ValFactory(key))))

其中 A、B 和 C 具有此签名:

TResult GetOrAdd(string key, Func<string, TResult> generator);

而且 Valfactory 有签名:

TResult Get(string key);

“链式”函数的数量没有限制,所以它可以是 A、B 或 A、B、C 或 A、B、C、D、E 等。

我将函数 A、B、C 等存储在 LinkedList 中

如何以我想要的方式称呼它们?

编辑添加一些信息以进行澄清:

我需要这个来实现多级缓存。有几个不同的缓存都实现了“GetOrAdd”功能。一级缓存只是一个 ConcurrentDictionary 已经有 GetOrAdd 函数。

缓存的工作方式是,如果一级缓存没有特定键的条目,它会尝试在二级缓存中查找。二级缓存在未命中等情况下查找三级缓存。

没有缓存应该知道另一个缓存,它应该只实现签名TResult GetOrAdd(string, Func&lt;string, TResult&gt; functionToCallOnCacheMiss)

【问题讨论】:

  • 你绝对应该展示一些代码。否则它的读心术。

标签: c# function recursion functional-programming


【解决方案1】:

如果您将每个 Func 存储在一个链表中,为什么不直接遍历该列表并使用前一个结果调用下一个 func?

【讨论】:

  • 这与我想要的相反 - 我希望当前节点从列表中的下一个节点获取它的值
  • 那么以相反的顺序循环遍历列表?我仍然对这是一个递归问题感到困惑?!听起来您更像是需要一个堆栈/函数队列。
【解决方案2】:

我已经为它做了一个小例子。它没有使用两个不同的委托,而是只使用一个来简化事情。确保作为参数传递的最后一个方法不会尝试访问队列是用户的责任。不过,它可能可以重新排列。

public delegate TResult GetOrAdd<TResult>(string key, Queue<GetOrAdd<TResult>> generatorChain);

static T ExecuteChain<T>(string key, params GetOrAdd<T>[] generators)
{
    var queue = new Queue<GetOrAdd<T>>(generators.Skip(1));
    return generators.First()(key, queue);
}

static int A(string key, Queue<GetOrAdd<int>> generatorChain)
{
    var newKey = key.ToUpper();
    //You can perform checks on the queue
    //i.e. if it's empty, throw an exception
    var tempResult = generatorChain.Dequeue()(newKey, generatorChain);
    return tempResult*2;
}

static int B(string key, Queue<GetOrAdd<int>> generatorChain)
{
    var newKey = key.Insert(0, "My string is: ");
    var tempResult = generatorChain.Dequeue()(newKey, generatorChain);
    return tempResult + 1;
}

static int ValFactory(string key, Queue<GetOrAdd<int>> generatorChain)
{
    //Instead of defining another delegate, we just don't use the queue
    //You can still run your checks (i.e. throw exception if not empty)
    return key.GetHashCode();
}

您可以通过以下方式调用链:

var result = ExecuteChain<int>("Hello world", A, B, ValFactory);

【讨论】:

    【解决方案3】:

    鉴于这些定义:

    public delegate TResult GetOrAdd<TResult>(string key, Func<string, TResult> generator);
    public delegate TResult Get<TResult>(string key);
    

    你需要一个这样的函数:

    public static Func<string, TResult> Chain<TResult>(Get<TResult> last, params GetOrAdd<TResult>[] funcs) {
        if (funcs.Count() == 0) return key => last(key);
        var head = funcs.First();
        var tail = funcs.Skip(1).ToArray();
        return key => head(key, Chain(last, tail));
    }
    

    这样写代码:

    var result = Chain(ValFactory, A, B, C)(key);
    

    请注意,这看起来像是 chain of responsibility 的类型。

    更新:您还可以简化Chain 并让客户端通过适配器函数使用所需的参数排序:

    public static GetOrAdd<TResult> Adapt<TResult>(Get<TResult> factory) {
        return (key, next) => factory(key);
    }
    
    public static Func<string, TResult> Chain<TResult>(params GetOrAdd<TResult>[] funcs) {
        if (funcs.Count() == 0) return null; // or return key => default(TResult);
        var head = funcs.First();
        var tail = funcs.Skip(1).ToArray();
        return key => head(key, Chain(tail));
    }
    
    ...
    
    var result = Chain(A, B, C, Adapt(ValFactory))(key);
    

    【讨论】:

    • 我会尝试这种方法,看看明天是否有效,这里是午夜。谢谢您的发布! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 2010-10-16
    • 2011-05-16
    • 1970-01-01
    相关资源
    最近更新 更多