【问题标题】:English ruler recursion issue英文标尺递归问题
【发布时间】:2020-01-27 00:55:36
【问题描述】:

我正在尝试从书中试运行英文标尺方法,但是试运行有点混乱,我的输出和原始输出不同。

public static void drawRuler(int nInches, int majorlength)
{
  drawLine(majorLength, 0);
  for (int j =1; j<= nInches, j++)
  {
   drawInterval(majorLength-1);
   drawLine(majorLength, j);
 }
}

private static void drawInterval (int centralLength)
{
 if (centralLength>=1)
 {
  drawInterval(centralLength - 1);
  drawLine(centralLength);
  drawInterval(centrakLength-1);
 }
}

public static void drawLine(int tickLength, int tickLabel)
{
 for (int j=0; j<ticklength; j++)
   System.out.print("-")
 if (tickLabel>=0)
  System.out.print(" "+tickLable);
 System.out.print("\n");
}

private static void drawLine(int tickLength)
{
  drawLine(tickLength, -1);
}

第一次,我输入nInches = 1 and majorlength 3

1- drawLine 将被调用 (3,0) // 刻度长度和刻度标签

public static void drawLine(3, 0)
    {
     for (int j=0; j<3; j++)
       System.out.print("-")
     if (tickLabel>=0)
      System.out.print(" "+tickLable);
     System.out.print("\n");
    }

输出:

--- 0

2- 现在下面的循环将从 drawRuler 函数运行

for (int j =1; j<=1, j++)
      {
       drawInterval(majorLength-1);

* Point1: 意思是上面的线会调用drawInterval(2) ?*

drawLine(majorLength, j);
     }

3- 我们移动到以 2 作为参数的函数 drawInterval

private static void drawInterval (2)
    {
     if (centralLength>=1)   // true
     {
      drawInterval(centralLength - 1);
 **Point 2: what the point of calling same function with 1 ? and will it call itself again without drawing anything? and function will go on nextline after drawInterval become 0?**


drawLine(centralLength);
      drawInterval(centrakLength-1);
     }
    }

Point3:drawLine(tickLength, -1); 为什么我们使用这个-1

【问题讨论】:

    标签: java function recursion


    【解决方案1】:

    您的代码似乎是从Data Structures and Algorithms in Java 书中获取的。

    但是您的代码甚至无法编译,有很多语法错误和变量用词不当。您是否通过扫描 + OCR 从书中获得了该源代码,而没有尝试使用 Java 运行它?

    那么首先让我们修复错误并将这段零碎的代码变成一个带有 main 方法的类,好吗?

    package de.scrum_master.stackoverflow;
    
    public class EnglishRuler {
      public static void main(String[] args) {
        drawRuler(2, 4);
      }
    
      public static void drawRuler(int nInches, int majorLength) {
        drawLine(majorLength, 0);
        for (int j = 1; j <= nInches; j++) {
          drawInterval(majorLength - 1);
          drawLine(majorLength, j);
        }
      }
    
      private static void drawInterval(int centralLength) {
        if (centralLength >= 1) {
          drawInterval(centralLength - 1);
          drawLine(centralLength);
          drawInterval(centralLength - 1);
        }
      }
    
      private static void drawLine(int tickLength, int tickLabel) {
        for (int j = 0; j < tickLength; j++)
          System.out.print("-");
        if (tickLabel >= 0)
          System.out.print(" " + tickLabel);
        System.out.print("\n");
      }
    
      private static void drawLine(int tickLength) {
        drawLine(tickLength, -1);
      }
    }
    

    drawRuler(1, 3) 打印:

    --- 0
    -
    --
    -
    --- 1
    

    drawRuler(1, 5) 打印:

    ----- 0
    -
    --
    -
    ---
    -
    --
    -
    ----
    -
    --
    -
    ---
    -
    --
    -
    ----- 1
    

    drawRuler(2, 4) 打印:

    ---- 0
    -
    --
    -
    ---
    -
    --
    -
    ---- 1
    -
    --
    -
    ---
    -
    --
    -
    ---- 2
    

    这一切都符合预期。现在让我们为程序添加一些可选的调试输出:

    package de.scrum_master.stackoverflow;
    
    public class EnglishRuler {
      private static boolean DEBUG = true;
      private static String indent = "";
    
      public static void main(String[] args) {
        drawRuler(1, 3);
      }
    
      public static void drawRuler(int nInches, int majorLength) {
        if (DEBUG)
          System.out.println("drawRuler(" + nInches + ", " + majorLength + ")");
        drawLine(majorLength, 0);
        for (int j = 1; j <= nInches; j++) {
          drawInterval(majorLength - 1);
          drawLine(majorLength, j);
        }
      }
    
      private static void drawInterval(int centralLength) {
        indent += "  ";
        if (DEBUG)
          System.out.println(indent + "drawInterval(" + centralLength + ")");
        if (centralLength >= 1) {
          drawInterval(centralLength - 1);
          drawLine(centralLength);
          drawInterval(centralLength - 1);
        }
        indent = indent.substring(2);
      }
    
      private static void drawLine(int tickLength, int tickLabel) {
        indent += "  ";
        if (DEBUG)
          System.out.println(indent + "drawLine(" + tickLength + ", " + tickLabel + ")");
        for (int j = 0; j < tickLength; j++)
          System.out.print("-");
        if (tickLabel >= 0)
          System.out.print(" " + tickLabel);
        System.out.print("\n");
        indent = indent.substring(2);
      }
    
      private static void drawLine(int tickLength) {
        drawLine(tickLength, -1);
      }
    }
    

    只要DEBUGfalse,这不会改变输出。如果将其设置为truedrawRuler(1, 3) 的日志将变为:

    drawRuler(1, 3)
      drawLine(3, 0)
    --- 0
      drawInterval(2)
        drawInterval(1)
          drawInterval(0)
          drawLine(1, -1)
    -
          drawInterval(0)
        drawLine(2, -1)
    --
        drawInterval(1)
          drawInterval(0)
          drawLine(1, -1)
    -
          drawInterval(0)
      drawLine(3, 1)
    --- 1
    

    你有一个自动生成的试运行版本。

    至于你的问题:

    首先,我输入nInches = 1majorlength = 3

    1) drawLine 将被调用 (3,0)tickLengthtickLabel

    正确。

    Point1:以上行的意思是调用drawInterval(2)

    正确。

    第 3 点:drawLine(tickLength, -1)。为什么我们使用这个-1

    因为在drawLine(int tickLength, int tickLabel) 它说:

        if (tickLabel >= 0)
          System.out.print(" " + tickLabel);
    

    因此,使tickLabel 的值小于零只是在我们不在主区间而是在其间较小的子区间时避免打印标签的一种方法。


    更新:我还根据递归级别为带有调试输出的程序版本添加了缩进,并更新了日志输出以进行缩进以便 OP 更好地理解。


    更新2:您可以通过内联便捷方法drawLine(int tickLength)来简化程序,如下所示:

      private static void drawInterval(int centralLength) {
        // ...
          drawInterval(centralLength - 1);
          drawLine(centralLength, -1);  // Note the additional ", -1"
          drawInterval(centralLength - 1);
        // ...
      }
    

    然后删除这个,因为它现在不再使用了:

      // Delete me!
      private static void drawLine(int tickLength) {
        drawLine(tickLength, -1);
      }
    

    更新 3: 因为您似乎很生气,以至于我没有为方便的方法 drawLine(int tickLength) 打印日志输出,这里是为该方法生成输出的原始程序的另一个扩展版本同样,现在完全复制您的笔和纸试运行:

    package de.scrum_master.stackoverflow;
    
    public class EnglishRuler {
      private static boolean DEBUG = true;
      private static String indentText = "";
    
      public static void main(String[] args) {
        drawRuler(1, 3);
      }
    
      public static void drawRuler(int nInches, int majorLength) {
        debugPrint("drawRuler(" + nInches + ", " + majorLength + ")");
        drawLine(majorLength, 0);
        for (int j = 1; j <= nInches; j++) {
          drawInterval(majorLength - 1);
          drawLine(majorLength, j);
        }
      }
    
      private static void drawInterval(int centralLength) {
        indent();
        debugPrint("drawInterval(" + centralLength + ")");
        if (centralLength >= 1) {
          drawInterval(centralLength - 1);
          drawLine(centralLength);
          drawInterval(centralLength - 1);
        }
        dedent();
      }
    
      private static void drawLine(int tickLength, int tickLabel) {
        indent();
        debugPrint("drawLine(" + tickLength + ", " + tickLabel + ")");
        for (int j = 0; j < tickLength; j++)
          System.out.print("-");
        if (tickLabel >= 0)
          System.out.print(" " + tickLabel);
        System.out.print("\n");
        dedent();
      }
    
      private static void drawLine(int tickLength) {
        indent();
        debugPrint("drawLine(" + tickLength + ")");
        drawLine(tickLength, -1);
        dedent();
      }
    
      private static void debugPrint(String message) {
        if (DEBUG)
          System.out.println(indentText + message);
      }
    
      private static void indent() {
        indentText += "  ";
      }
    
      private static void dedent() {
        indentText = indentText.substring(2);
      }
    }
    

    更新后的控制台日志变为:

    drawRuler(1, 3)
      drawLine(3, 0)
    --- 0
      drawInterval(2)
        drawInterval(1)
          drawInterval(0)
          drawLine(1)
            drawLine(1, -1)
    -
          drawInterval(0)
        drawLine(2)
          drawLine(2, -1)
    --
        drawInterval(1)
          drawInterval(0)
          drawLine(1)
            drawLine(1, -1)
    -
          drawInterval(0)
      drawLine(3, 1)
    --- 1
    

    我觉得没有必要,但如果它对你有帮助,我很高兴。

    【讨论】:

    • 感谢您的解释。是的,我从书中获取了代码,但我没有在工具上尝试它,因为我正在尝试学习试运行。我留下的唯一查询是在这种情况下` if (centralLength >= 1) { drawInterval(centralLength - 1); drawLine(centralLength);` 当drawinterval(centralLength-1) 使中心长度值等于0 那么它将如何运行drawLine(centralLength); 因为条件将变为假,因为中心长度不大于或等于1。if (centralLength &gt;= 1)
    • 来自代码:drawInterval(centralLength - 1); drawLine(centralLength); drawInterval(centralLength - 1);,第 1 行 (drawInterval(centralLength - 1);) 使 central length=1 然后 drawLine 将其称为 1 输出 1 然后第 3 行 (drawInterval(centralLength - 1);) 将执行为1-1=0 所以现在控制会回到循环?不是吗?
    • 我同意基本算法的试运行是一个很好的学习工具,但你不能通过想象来学习驾驶,也不能只通过试运行来学习编程。因此,我鼓励您将代码输入到编辑器或 IDE 中,编译、运行、调试和分析它。你有更好的学习机会和更短的反馈周期,而不是从一本书中复制源代码到一个 SO 问题中,提出我已经通过打印调试日志回答的理论问题。 (待续……)
    • 关于你的问题,我不明白。作为一名程序员,您需要学会用散文(或让代码说话)更准确地表达自己。我读过你的 cmets 超过 3 倍,但我只能猜测你可能会问什么。我确信如果我理解了这个问题,我可以回答它,因为算法非常简单,并且在你的书中也得到了很好的解释。
    • 更新3:因为你好像很生气,我没有打印日志输出方便的方法drawLine(int tickLength),这里是原程序的另一个扩展版本也为该方法生成输出,现在完全复制您的笔和纸试运行 - 见上文。我现在已经完成了。要么你决定我应该得到你的赏金,要么你可以等待有人更好地解释它。我已经想不出如何解释更多了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 2013-05-05
    • 2017-03-28
    • 2011-12-10
    • 2011-08-01
    • 2023-04-10
    相关资源
    最近更新 更多