6

私は現在、ロアの作成を支援するプログラムを作成しています。すべての Book オブジェクトは親になり、子を持つことができます。これは、すべての子供が子供などを無限に持つことができることを意味します。再帰を使用してこれを説明できる ToString() メソッドに取り組んでいましたが、StackOverflowException が発生し続けます。

それが何を意味するかはわかっていますが、それをどのように修正するかについては疑問です。私は C# にはかなり慣れていませんが、Java の経験はかなりあります。そのため、私が見逃したトリックや何かを知っている場合はお知らせください。

私の質問は次のとおりです。StackOverflow 例外を回避するにはどうすればよいですか? 問題は GetAllChildren() にあります

編集:

テストを実行すると、次のようになります。

Name: a
Children:
b
c
    d
e

@lcからのコードで。次の出力が得られます。

Name: a
Children: No Children   b
c
e
    b
c
e
    b
c
e

クラスは次のとおりです。

class Book
{
    private String name;
    private Book[] children;
    private StringBuilder text;
    private Boolean isParent;

    public Book(String name, Book[] children, StringBuilder text, Boolean isParent)
    {
        this.name = name;
        this.children = children;
        this.text = text;
        this.isParent = isParent;
    }

    /**
     * Most likely all possible Constructors
     * */
    public Book(String name, Book[] children) : this(name, children, new StringBuilder("No Text"), true) { }
    public Book(String name, String text) : this(name, new Book[0], new StringBuilder(text), false) { }
    public Book(String name, StringBuilder text) : this(name, new Book[0], text, false) { }
    public Book(String name) : this(name, new Book[0], new StringBuilder("No Text"), false) { }
    public Book(Book[] children, String text) : this("Unnamed Book", children, new StringBuilder(text), true) { }
    public Book(Book[] children, StringBuilder text) : this("Unnamed Book", children, text, true) { }
    public Book(Book[] children) : this("Unnamed Book", children, new StringBuilder("No Text"), true) { }
    public Book(StringBuilder text) : this("Unnamed Book", new Book[0], text, false) { }
    public Book() : this("Unnamed Book", new Book[0], new StringBuilder("No Text"), false) { }

    public String Name
    {
        get { return name; }
        set { name = value; }
    }

    public Book[] Children
    {
        get { return children; }
        set { children = value; }
    }

    /**
     * Will Return the StringBuilder Object of this Text
     * */

    public StringBuilder Text
    {
        get { return text; }
        set { text = value; }
    }

    public Boolean IsParent
    {
        get { return isParent; }
        set { isParent = value; }
    }

    private void GetAllChildren(Book book, StringBuilder sb)
    {
        if (book.isParent)
        {
            GetAllChildren(book, sb);
        }
        else
        {
            sb.Append("\t");
            foreach (Book b in children)
            {
                sb.Append(b.Name + "\n");
            }
        }
    }

    public override String ToString()
    {
        StringBuilder sChildren = new StringBuilder("No Children");
        if (children.Length != 0)
        {
            GetAllChildren(this, sChildren);
        }

        return "Name: " + name + "\n" +
            "Children: " + sChildren.ToString();
    }
}
4

3 に答える 3

8

私はあなたが意味したと思います:

if (book.isParent)
{
    foreach (var child in book.Children)
        GetAllChildren(child, sb);
}

GetAllChildrenそれ以外の場合は、同じパラメーター ( book, sb) を使用してメソッドを何度も呼び出すだけです。


GetAllChildren補足事項 - の停止条件が子を反復処理しているため、そうすべきでない場合 (親でない場合は子を持たないはずです)、まだいくつかの問題があります。代わりに、独自の名前を返す必要があります。さらに、各子も上記の foreach ループにその名前を追加する必要があります (または、実際には、各ブックに独自の名前を追加する必要があります)。

補足 2 - 記述されたメソッド (これらの変更を含む) は、特定のインスタンスに関連していないため、実際には静的である必要があります (これにより、以下の提案が得られます)。


提案 - 代わりに次のようなものをお勧めします (テストされておらず、書式設定の作業が必要になります)。

//name changed to reflect what it really does
//also changed to be an instance method (we no longer pass in a Book)
//added listThisBooksName parameter to allow supressing the topmost book's output
private void AppendAllChildren(StringBuilder sb, int level = 0, 
    bool listThisBooksName = false)
{
    if (listThisBooksName)
    {
        //append ourself here

        //first indent however far we need to
        sb.Append(new String('\t', level));

        //now add our name
        sb.Append(this.Name);

        //and a newline (you can strip the last one later if you want)
        sb.Append('\n');
    }

    //forget the "isParent" property, just check if it has any children
    //we don't need Children.Any() because the foreach will just iterate 0 times
    //you might also consider using a List<Book> instead of an array for Children
    if (this.Children != null)
        foreach (var child in this.Children)
            child.AppendAllChildren(sb, level+1, true);
}
于 2013-03-14T09:16:36.433 に答える
2

これは問題ではありませんか:

    if (book.isParent)
    {
        GetAllChildren(book, sb);
    }

同じメソッドをもう一度呼び出しますか?GetAllChildren上記は子を繰り返し処理し、各子を呼び出す必要があると思いますBookBook 子がいない場合にのみ名前を出力します。

于 2013-03-14T09:17:18.413 に答える
2

その本の IsParent が真である間、再帰は同じ本で再帰しています。本が親である場合、おそらくすべての子を再帰したいと思うでしょう。

于 2013-03-14T09:17:33.813 に答える