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

时间:2011-08-15 22:00:35

标签: c# recursion ref-parameters

似乎每次我去写一个递归函数我最终都会返回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)

4 个答案:

答案 0 :(得分:3)

我猜是这样的:

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

答案 1 :(得分:2)

首先,首先是ref参数绝对没有意义。您很可能无法理解ref参数 - 请参阅my article on this topic

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

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,请!)

答案 2 :(得分:2)

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

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

    return retDocs;
}

答案 3 :(得分:1)

嗯,我有一个偶尔使用的模式,我想把它作为一个选项展示。然而,当我试图按照书面处理它时,我的大脑有点剔除,所以我们达成了一个协议(我的大脑和我),我们只想找出一个简单的版本来向你展示。

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

    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,它不是分支,但它是一个简洁的例子,当非变异递归很容易实现并且比我试图通过改变{{1 }}。

实际上,您的特定示例似乎更好(对我而言)作为两步解决方案,在开头创建了一个List返回值。 :)