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

本文关键字:过程 递归 转换 返回 递归函数 列表 副作用 ref param | 更新日期: 2023-09-27 18:05:34

似乎每次编写递归函数时,我都会让它返回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)

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

我想是这样的:

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

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

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

首先,作为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);
    }
}

你可以以递归的方式保持它,并在每个级别创建一个新的列表,但我觉得它有点丑陋。上面为你提供了你想要的公共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!(

好吧,我有一个偶尔使用的模式,我想把它作为一个选项显示出来。然而,当我试图按照书面形式处理它时,我的大脑有点紧张,所以我们(我和我的大脑(达成了一个协议,我们只会想出一个简单的版本给你看。

它甚至可能不太适用于你的特定问题,但这是我过去想要以不可变的方式完成事情时使用的一种方式,这似乎正是你想要的。

    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,也没有分支,但它是一个简洁的例子,说明了与试图通过改变StringBuilder来实现相同的东西相比,非变异递归更容易实现,(对我来说(更容易理解。

实际上,您的特定示例(对我来说(似乎更适合作为两步解决方案,一开始只创建一个List返回值。:(