Ruby on Rails的递归树

4
以下是用html.erb完成的迭代树,它只能到达树结构中的两个级别:
      <ul>
    <li><%= root_template.id  %></li>
      <ul>
          <% for template in root_template.children  %>
              <li><%= template.id  %></li>
              <% if template.has_children? %>
                <ul>
                <% for template_child in template.children  %>
                    <li><%= template_child.id  %></li>
                <% end %>
                </ul>
              <% end %>
          <% end %>
      </ul>
  </ul>

结果: 迭代

我希望将代码移动到辅助文件中,并应用递归来达到所有级别:

html.erb(因此,从模板设置根):

  <% html = '' %>
  <ul>
    <li><%= root_template.id  %></li>
    <ul>
        <%= recursive_tree root_template, html %>
    </ul>
  </ul>

然后是辅助方法:

  def recursive_tree(root, html)
    html << ''
    if !root.has_children?
      html << "<li>#{root.id}</li>"
      return html.html_safe
    else
      for template_child in root.children
        html << "<ul>#{recursive_tree(template_child, html)}</ul>"
      end
    end
    return html.html_safe
  end

结果:错误的递归结果

我已经花费了一天的时间来确定如何从辅助程序向模板发送适当的html,现在即使我使用调试器也找不出这个递归的问题和解决方案。有什么建议吗?


结果看起来一模一样。问题出在哪里? - Helio Santos
@HélioSantos 我已经纠正了第一张图片。 - Baris
2个回答

3
以下是我遇到的递归问题的最终答案,它们都有类似以下模板的调用:
对于 sol 1:
  <% html = '' %>
  <ul>
    <%= recursive_tree root_template, html %>
  </ul>

对于sol 2:
  <ul>
    <%= call_me_baby_two root_template %>
  </ul>

解决方案1) 使上述有问题的代码在不需要任何清理的情况下工作:

  • Explanation 1) I was passing whole html code to recursive call, so it was the biggest mistake, now I pass a blank string and append it after recursive call returns to the main html, and it goes like that for deeper calls in recursion.
  • Explanation 2) I wasn't adding the root's id if it a children, I've realized this after solving the previous issue.

    def recursive_tree(root, html)
    html << ''
    if !root.has_children?
      html << "<li>#{root.id}</li>"
      return html.html_safe
    else
      html << "<li>#{root.id}</li>" # Explanation 2
      for template_child in root.children 
        temp_html = '' # Explanation 1
        html << "<ul>#{recursive_tree(template_child, temp_html)}</ul>"
      end
    end
    return html.html_safe
    end
    

解决方案2) 我还花了额外的时间让@davidrac的伪代码能够正常工作:

  def recursive_tree_three(root)
    html=''
    if root
      html = "<li>#{root.id}</li>"
      if root.has_children?
        for template_child in root.children
          html << "<ul>"
          html << recursive_tree_three(template_child)
          html << "</ul>"
        end
      end
    end
    return html
  end

  def call_me_baby_two(root)
    recursive_tree_three(root).html_safe
  end

1

我能发现的一个问题是你使用了比必要更多的html_safe。由于你在递归方法中使用了html_safe,所以它会在更深层次的节点中被反复调用。

另一个看起来有问题的地方是你的实现中<li>标签没有正确包含<ul>标签,因此对于具有多个子节点的节点可能会得到错误的结果。

也许你可以稍微不同地构建递归方法(主要是伪代码):

def recursive_tree(root)
  res = ''
  if root
    res = "<li>#{root.id}"
    # if root has children
    #   add <ul>
    #   for each of the children res << recursive_tree(child)
    #   add </ul>
    # end
    res << "</li>"
  end
  res
end

现在添加一些包装函数来创建初始列表并添加html_safe:
wrapping_func(root)
  "<ul>#{recursive_tree(root)}</ul>".html_safe
end

顺便提一句,如果您在问题中添加所期望的结构以及HTML输出内容,可能会有所帮助。


添加包装方法并删除html_safe对结果没有任何影响。在开始时,您编写了res = ''而不是res << '',这也使得每次递归调用时重置res的内容,因此res仅具有递归深度的最后一次调用的内容。 - Baris
@baris,在我的版本中,res是一个局部变量,而不是传递给函数的,因此在开头声明它不会覆盖任何内容。每次调用函数都会创建一个内部变量,并将递归调用的结果附加到其中。如果您分享更改后的代码和传递给它的数据,我可以帮助您找出问题所在。 - davidrac
你实现中似乎有一个错误,就是<li>标签没有正确地包含<ul>标签,因此对于具有多个子节点的节点,可能会得到错误的结果。 - davidrac
  1. 是的,我错了!因为我在我的实现中将“res”传递给了递归函数,所以错误就出在那里。
  2. <ul> 包含 <il>,对于标签来说似乎没有问题。最终,我结合了您的伪代码和我的实现,程序正常运行。我会单独写一篇文章。谢谢!
- Baris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接