如何将对 ref 参数有副作用的递归过程转换为返回列表的递归函数?

发布于 2024-11-29 20:57:52 字数 1538 浏览 1 评论 0原文

似乎每次我去编写递归函数时,我最终都会使其返回 void 并使用 ref 参数。

我更愿意编写一个只返回结果列表的函数。

如果答案很简单,我很抱歉 - 由于某种原因它让我困惑。

这是我现在的代码:

public static void GetResrouces(string startURL, ref List<XDocument> result)
{
    var doc = XDocument.Parse(GetXml(startURL)); // GetXml ommitted - returns xml string
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            GetResrouces(startURL + item.location, ref result);
        }
    }
    else
    {
        result.Add(doc);
    }
}

public partial class resourceList
{

    private resourceListResourceURL[] resourceURLField;

    private string locationField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("resourceURL")]
    public resourceListResourceURL[] resourceURL
    {
        get
        {
            return this.resourceURLField;
        }
        set
        {
            this.resourceURLField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType = "anyURI")]
    public string location
    {
        get
        {
            return this.locationField;
        }
        set
        {
            this.locationField = value;
        }
    }
}

我想知道是否可以将其重写为原型:

public static List<XDocument> GetResources(string startURL)

It seems every time I go to write a recursive function I end up making it return void and using a ref parameter.

I'd much rather be able to write a function that just returns a result list.

Apologies if the answer is very simple - for some reason it elludes me.

Here's the code I have now:

public static void GetResrouces(string startURL, ref List<XDocument> result)
{
    var doc = XDocument.Parse(GetXml(startURL)); // GetXml ommitted - returns xml string
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            GetResrouces(startURL + item.location, ref result);
        }
    }
    else
    {
        result.Add(doc);
    }
}

public partial class resourceList
{

    private resourceListResourceURL[] resourceURLField;

    private string locationField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("resourceURL")]
    public resourceListResourceURL[] resourceURL
    {
        get
        {
            return this.resourceURLField;
        }
        set
        {
            this.resourceURLField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute(DataType = "anyURI")]
    public string location
    {
        get
        {
            return this.locationField;
        }
        set
        {
            this.locationField = value;
        }
    }
}

I'd like to know if it can be rewritten to the prototype:

public static List<XDocument> GetResources(string startURL)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

南冥有猫 2024-12-06 20:57:52

我猜是这样的:

public static List<XDocument> GetResources(string startURL)
{
    var result = new List<XDocument>();
    var doc = XDocument.Parse(GetXml(startURL));
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            result.AddRange(GetResources(startURL + item.location));
        }
    }
    else
    {
        result.Add(doc);
    }
    return result;
}

I guess something like:

public static List<XDocument> GetResources(string startURL)
{
    var result = new List<XDocument>();
    var doc = XDocument.Parse(GetXml(startURL));
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            result.AddRange(GetResources(startURL + item.location));
        }
    }
    else
    {
        result.Add(doc);
    }
    return result;
}
穿透光 2024-12-06 20:57:52

代码看起来很好(减去参数上不必要的 ref)。一种选择是将递归方法包装在非递归同伴中:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> retDocs = new List<XDocument>();
    GetResources(startURL, retDocs);

    return retDocs;
}

The code looks fine as is (minus the unnecessary ref on the parameter.) One option is to wrap the recursive method in a non-recursive companion:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> retDocs = new List<XDocument>();
    GetResources(startURL, retDocs);

    return retDocs;
}
野生奥特曼 2024-12-06 20:57:52

一方面,作为 ref 参数一开始就没有任何意义。您很可能不理解 ref 参数 - 请参阅我的文章关于这个主题

由于这是自然递归的,我可能会这样写:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> ret = new List<XDocument>();
    GetResourcesRecursive(startURL, ret);
    return ret;
}

private static void GetResourcesRecursive(string startURL,
                                          List<XDocument> result)
{
    var doc = XDocument.Parse(GetXml(startURL));
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            GetResourcesRecursive(startURL + item.location, ref result);
        }
    }
    else
    {
        result.Add(doc);
    }
}

您可以以递归方式保持它并在每个级别创建一个新列表,但对我来说有点难看。上面为您提供了您想要的 public API,但没有向左、右和中心分配集合。

现在您可以以非递归方式编写它,基本上是通过创建一个要处理的 URL 队列来完成:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> ret = new List<XDocument>();
    Queue<string> urls = new Queue<string>();
    urls.Enqueue(startUrl);
    while (urls.Count > 0)
    {
        string url = urls.Dequeue();
        var doc = XDocument.Parse(GetXml(url));
        var xs = new XmlSerializer(typeof(resourceList));
        var rdr = doc.CreateReader();
        if (xs.CanDeserialize(rdr))
        {
            var rl = (resourceList) xs.Deserialize(doc.CreateReader());

           foreach (var item in rl.resourceURL)
           {
               queue.Enqueue(url + item.location);
           }
        }
        else
        {
            ret.Add(doc);
        }  
    }
    return ret;
}

对于我来说,现在已经太晚了,无法确定这是否以相同的顺序给出结果- 我怀疑不是 - 但希望这并不重要。

(您真的没有一个名为resourceList的类型吗?ResourceList,拜托!)

For one thing, there's absolutely no point in that being a ref parameter in the first place. It's quite possible that you don't understand ref parameters - see my article on this topic.

As this is naturally recursive, I'd probably write it like this:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> ret = new List<XDocument>();
    GetResourcesRecursive(startURL, ret);
    return ret;
}

private static void GetResourcesRecursive(string startURL,
                                          List<XDocument> result)
{
    var doc = XDocument.Parse(GetXml(startURL));
    var xs = new XmlSerializer(typeof(resourceList));
    var rdr = doc.CreateReader();
    if (xs.CanDeserialize(rdr))
    {
        var rl = (resourceList)xs.Deserialize(doc.CreateReader());

        foreach (var item in rl.resourceURL)
        {
            GetResourcesRecursive(startURL + item.location, ref result);
        }
    }
    else
    {
        result.Add(doc);
    }
}

You can keep it in a recursive fashion and create a new list at every level, but it feels a little ugly to me. The above gives you the public API you want, but without allocating collections left, right and centre.

Now you could write it in a non-recursive fashion, basically by creating a queue of URLs to work through:

public static List<XDocument> GetResources(string startURL)
{
    List<XDocument> ret = new List<XDocument>();
    Queue<string> urls = new Queue<string>();
    urls.Enqueue(startUrl);
    while (urls.Count > 0)
    {
        string url = urls.Dequeue();
        var doc = XDocument.Parse(GetXml(url));
        var xs = new XmlSerializer(typeof(resourceList));
        var rdr = doc.CreateReader();
        if (xs.CanDeserialize(rdr))
        {
            var rl = (resourceList) xs.Deserialize(doc.CreateReader());

           foreach (var item in rl.resourceURL)
           {
               queue.Enqueue(url + item.location);
           }
        }
        else
        {
            ret.Add(doc);
        }  
    }
    return ret;
}

It's too late in the day for me to work out whether this gives the results in the same order - I suspect it doesn't - but hopefully that's not important.

(You don't really have a type called resourceList do you? ResourceList, please!)

世态炎凉 2024-12-06 20:57:52

嗯,我有一个偶尔使用过的模式,我想将它作为一个选项来展示。然而,当我试图按照书面方式解决它时,我的大脑有点不舒服,所以我们达成了一项协议(我的大脑和我),我们只会找出一个简单的版本来向您展示。

它甚至可能不太适用于您的具体问题,但这是我过去使用的一种方式,当我希望以不可变的方式完成事情时,这似乎正是您所寻找的。

    public string IntCSVReverse(List<int> IntList)
    {
        return IntCSVReverse_recurse(IntList, 0);
    }

    private string IntCSVReverse_recurse(List<int> IntList, int Index)
    {
        if (Index == (IntList.Count - 1))
            return IntList[Index].ToString();
        else
            return
                IntCSVReverse_recurse(IntList, Index + 1)
                + "," + IntList[Index].ToString();
    }

所以,这个模式是有其价值的。它不是 XML,它没有分支,但它是一个简洁的示例,说明非变异递归很容易实现,并且(对我来说)比尝试通过变异 来实现相同的事情更容易理解字符串生成器。

确实,您的特定示例似乎(对我来说)更好地作为两步解决方案,在开始时创建了一个列表返回值。 :)

Well, I have a pattern I have used on occasion, and I wanted to show it as an option. However, my brain was a bit ticked off when I tried to tackle it as written, so instead we came to an agreement (my brain and I) that we would just figure out a simple version to show you.

It may not even be quite applicable to your specific question, but it's one way I have used in the past when I wanted things done in a non-mutable fashion, which seemed to be what you were looking for.

    public string IntCSVReverse(List<int> IntList)
    {
        return IntCSVReverse_recurse(IntList, 0);
    }

    private string IntCSVReverse_recurse(List<int> IntList, int Index)
    {
        if (Index == (IntList.Count - 1))
            return IntList[Index].ToString();
        else
            return
                IntCSVReverse_recurse(IntList, Index + 1)
                + "," + IntList[Index].ToString();
    }

So, there is the pattern, for what it's worth. It's not XML, it doesn't branch, but it's a succinct example of when a non-mutating recursion is easy to implement and is more understandable (to me) than trying to implement the same thing by, say, mutating a StringBuilder.

Really, your particular example seems to flow better (to me) as the two-step solution with a single List return value created at the beginning. :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文