如何将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)
我想是这样的:
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返回值。:(