Как преобразовать рекурсивную процедуру с побочными эффектами ref param в рекурсивную функцию, возвращающую список? - PullRequest
2 голосов
/ 16 августа 2011

Кажется, что каждый раз, когда я пишу рекурсивную функцию, я заканчиваю тем, что возвращаю ее как 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 ]

3 голосов
/ 16 августа 2011

Я думаю, что-то вроде:

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;
}
2 голосов
/ 16 августа 2011

Код выглядит как есть (за исключением ненужного ref для параметра.) Один из вариантов - обернуть рекурсивный метод в нерекурсивный компаньон:

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

    return retDocs;
}
2 голосов
/ 16 августа 2011

Во-первых, абсолютно бессмысленно быть параметром 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, пожалуйста!)

1 голос
/ 16 августа 2011

Ну, у меня есть шаблон, который я использовал в некоторых случаях, и я хотел показать его в качестве опции. Тем не менее, мой мозг был немного раздражен, когда я пытался справиться с этим, как написано, поэтому вместо этого мы пришли к соглашению (мой мозг и я), что мы просто придумаем простую версию, чтобы показать вам.

Возможно, это даже не совсем применимо к вашему конкретному вопросу, но это один из способов, которым я пользовался в прошлом, когда хотел, чтобы все было сделано не изменяемым образом, что, казалось, было тем, что вы искали.

    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, созданным в начале. :)

...