循环访问文本文件时,For 循环比预期更早终止

For loop terminates earlier than expected when iterating over a text file

提问人:oresky 提问时间:3/21/2023 更新时间:3/21/2023 访问量:40

问:

我一直在读这本书,Sedgewick 和 Wayne 的算法,这是第 1 章中给出的练习之一:

1.3.4 编写一个堆栈客户端括号,用于从标准输入中读取文本流,并使用堆栈来确定其括号是否正确平衡。例如,程序应为 [()]{}{()()()} 打印 true,为 [(]) 打印 false。

现在,我的解决方案在使用 while 循环时有效(尽管它并不完全按照要求执行):

链接到In.java

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;

public class Parenthesis {
    public static void main(String[] args) {
        In input = new In("parenthesis.txt");
        LinkedListStack<String> stack = new LinkedListStack<>();

        while (!input.isEmpty()) {
            String s = input.readLine();
            
            if (s.equals("("))
                stack.push(s);
            else if (s.equals(")"))
                if (!stack.isEmpty())
                    stack.pop();
                else
                    stack.push(s);
        }

        if (stack.isEmpty())
            StdOut.println("Balanced");
        else
            StdOut.println("Unbalanced");
    }
}

但是当我用 for 循环替换 while 循环时,循环在到达最后一个“)”之前就终止了:

import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;

public class Parenthesis {
    public static void main(String[] args) {
        In input = new In("parenthesis.txt");
        LinkedListStack<String> stack = new LinkedListStack<>();

        for (String s = input.readLine(); !input.isEmpty(); s = input.readLine()) {
            if (s.equals("("))
                stack.push(s);
            else if (s.equals(")"))
                if (!stack.isEmpty())
                    stack.pop();
                else
                    stack.push(s);
        }
    
        if (stack.isEmpty())
            StdOut.println("Balanced");
        else
            StdOut.println("Unbalanced");
    }
}

我以为我使用的 for 循环只是 while 循环的紧凑版本,但显然我做错了什么。我尝试一遍又一遍地调试代码,但无法弄清楚为什么会发生这种情况。

P.S. 如果有更有效的方法来解决这个问题,请分享。谢谢!

for-loop while-loop 堆栈 java.util.scanner

评论

0赞 Jens 3/21/2023
你总是读两行for (String s = input.readLine(); !input.isEmpty(); s = input.readLine()) {
0赞 queeg 3/21/2023
什么?edu.princeton.cs.algs4.In
0赞 oresky 3/21/2023
@Jens我怀疑是这样,但我真的不知道为什么。据我了解,初始化到第一行,检查当前行是否为空,第二次迭代后更新下一行。所以它应该迭代读取所有行......String s = input.readLine()s!input.isEmpty()input.readLine()s
0赞 oresky 3/21/2023
此处使用的类的源代码@QueegIn
0赞 Jens 3/21/2023
@oresky 如果不了解IN的实现,很难说。但我建议你使用调试器来找出发生了什么

答:

0赞 tgdavies 3/21/2023 #1

因为 for 循环在执行增量后会检查其终止条件,如果它为 false,则会终止它,因此永远不会使用 which 将 移动到空的结果。readLineIn

运行此代码以查看:

public class X {
    public static void main(String[] args) {
        In in = new In();
        for (String s = in.readLine(); !in.isEmpty(); s = in.readLine()) {
            System.out.println("body");
        }
    }

}
class In {
    private int count = 0;
    public String readLine() {
        System.out.println("readLine " + count);
        return ""+count++;
    }

    public boolean isEmpty() {
        System.out.println("isEmpty " + count);
        return count > 2;
    }
}

输出为:

readLine 0
isEmpty 1
body
readLine 1
isEmpty 2
body
readLine 2
isEmpty 3

看到最后一个 .bodyreadLine

程序中的另一个错误是至少调用一次 - 即使 已经是空的。readLineIn

如果你真的想使用一个循环,你可以创建一个并使用一个foreach循环:forIterable

import java.util.Iterator;

public class X {
    public static void main(String[] args) {
        In in = new In();
        for (String s: new InIterable(in)) {
            System.out.println("body " + s);
        }
    }

}

class InIterable implements Iterable<String> {
    private final In in;

    InIterable(In in) {
        this.in = in;
    }

    @Override
    public Iterator<String> iterator() {
        return new Iterator<>() {
            @Override
            public boolean hasNext() {
                return !in.isEmpty();
            }

            @Override
            public String next() {
                return in.readLine();
            }
        };
    }
}

评论

0赞 oresky 3/21/2023
有没有办法在递增之前进行 for 循环检查条件?