我的任务:


编写嵌套的for循环以每行产生以下输出
48个字符宽:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~~+~
+~++~++~++~++~++~++~++~++~++~++~++~++~++~++~++~+     
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



这是我的主意,但我想那是多余的。如果是这样,如何在这里最小化冗余?顺便说一下,我的输出是正确的。

public class Exercises {
    public static void main (String[] args){

    for (int i = 1; i <= 4; i++){
        for (int j = 1; j <= 12; j++){
            System.out.print("~");
        }
    }
            System.out.println();
            System.out.print("~");

    for (int i = 1; i <=15; i++){
            System.out.print("+~~");
    }
            System.out.println("+~");
            System.out.print("+~");
    for (int i = 1; i <= 15; i++){
            System.out.print("++~");
        }
            System.out.println("+");

    for (int i = 1; i <= 4; i++){
        for (int j = 1; j <= 12; j++){
            System.out.print("~");
        }
    }
  }
}


评论

您的缩进真的很奇怪。

#1 楼


for (int i = 1; i <= 4; i++){
    for (int j = 1; j <= 12; j++){
        System.out.print("~");
    }
}
System.out.println();



可以很容易地是:

for (int i = 0; i < 48; i++) {
    System.out.print('~');
}
System.out.println();


一些编辑:


改进的格式:从){) {

简化为一个从for047循环(从0开始,运行到小于48
打印单个字符而不是一个字符包含一个字符的字符串以提高性能

此:


        System.out.print("~");

for (int i = 1; i <=15; i++){
        System.out.print("+~~");
}
        System.out.println("+~");



可以是这样的:

for (int i = 0; i < 16; i++){
    System.out.print("~+~");
}
System.out.println();


更改之处:


更好的格式化
循环并打印~+~模式


        System.out.print("+~");
for (int i = 1; i <= 15; i++){
        System.out.print("++~");
    }
        System.out.println("+");



至:

for (int i = 0; i < 16; i++){
    System.out.print("+~+");
}
System.out.println();


所做的更改:


>更好的格式化
循环并打印+~+模式

所有修补程序都有一些共同点:


缩进和格式化
查找正确的模式

其他更改:




使用方法:

当前所有代码在main方法中。将其拆分。


最终代码:

public class Exercises {

    public static void main (String[] args){
        printTildes();
        printTPT_Pattern();
        printPTP_Pattern();
        printTildes();
    }

    private static void printTildes() {
        for (int i = 0; i < 48; i++){
            System.out.print("~");
        }
        System.out.println();
    }

    private static void printTPT_Pattern() {
        for (int i = 0; i < 16; i++){
            System.out.print("~+~");
        }
        System.out.println();
    }

    private static void printPTP_Pattern() {
        for (int i = 0; i < 16; i++){
            System.out.print("+~+");
        }
        System.out.println();
    }

}


评论


\ $ \ begingroup \ $
“仅打印一个字符,以提高性能” —请停止鼓励微优化。建议这样的“改进”会使人们相信过早的优化是个好主意。在这种情况下,我认为与其余代码的一致性要重要得多。而且,实际上,至少在OpenJDK中,print(char)委托给print(String)而不是相反,因此,可忽略的性能提高是在另一个方向上。
\ $ \ endgroup \ $
– wchargin
15年11月26日在22:20

\ $ \ begingroup \ $
这种回答被接受了,但不满足任务要求。明确要求“写嵌套循环”以产生给定的输出,答案没有嵌套循环。 -1
\ $ \ endgroup \ $
–CiaPan
2015年11月27日7:34



\ $ \ begingroup \ $
这3种方法非常接近,可以重构为一种。
\ $ \ endgroup \ $
–叙利亚
15年11月27日在8:23



#2 楼

您并不是以最合乎逻辑的方式分解模式。每行仅包含三个字符的重复块。

public class SquigglePlus {
    private static final String[] PATTERNS = { "~~~", "~+~", "+~+", "~~~" };

    public static void main(String[] args) {
        for (int line = 0; line < PATTERNS.length; line++) {
            for (int col = 0; col < 48; col += PATTERNS[line].length()) {
                System.out.print(PATTERNS[line]);
            }
            System.out.println();
        }
    }
}


或者,内部循环可以数到16:for (int i = 0; i < 16; i++) { … }。请注意,在Java中,这是最常见的计数方式:从0开始,并使用<进行终止检查。另一种方法(for (int i = 1; i <= 16; i++) { … })不会出错,但是通常只有在有特殊原因的情况下,才这样写。

更好的方法是,使用现代的增强型for循环代替旧的-样式计数循环。

public class SquigglePlus {
    private static final String[] PATTERNS = { "~~~", "~+~", "+~+", "~~~" };

    public static void main(String[] args) {
        for (String pattern : PATTERNS) {
            for (int col = 0; col < 48; col += pattern.length()) {
                System.out.print(pattern);
            }
            System.out.println();
        }
    }
}


评论


\ $ \ begingroup \ $
由于您显式地计算出打印出的字符,因此不需要保持等长的长度-您可以将每个字符缩至最短的时间:PATTERNS = {“〜”,“〜+〜”,“ +〜 +“,”〜“};
\ $ \ endgroup \ $
–CiaPan
15年11月27日在7:30

\ $ \ begingroup \ $
一种不太常用的方法是向下计数:for(int charstowrite = 48; charstowrite> 0; charstowrite-= pattern.length())这里似乎没有任何好处,但是如果您得到一些长度变量而不是48常数,它允许循环条件表达式将计数变量与常数0进行比较,而不是比较两个变量。
\ $ \ endgroup \ $
–CiaPan
15年11月27日在12:15

#3 楼

一般情况

您有两个像这样的循环:


for (int i = 1; i <= 4; i++){
    for (int j = 1; j <= 12; j++){
        System.out.print("~");
    }
}



这只是嵌套循环为了嵌套循环。我知道需求状态规定要使用嵌套循环,但是您不应仅出于此目的而故意使代码效率低下。这应该只是for (int i = 1; i <= 48; i++)


您正在做很多事情。如果您使用类似System.out.print()之类的代码,并在其后附加字符,并且在完成后仅在末尾打印一次,则代码将更加清晰:

方法

您所采用的方法并不理想,正如其他答案所解决的那样。对于如何解决这样的问题,我将采取一种全新的方法。因为它们永远不会改变。


行长(48)
行数(4)
每行都有自己的模式(模式本身不是很重要)

我们可以做一些基本的扶手椅数学运算,只需看一下线条就可以推断出一些东西。


48可被2、3、4整除8、12、16等。
如果要均匀地划分线段,则模式必定会成为其中的一种。模式是3。



所以,让我们在顶部声明所有这些:

class Exercises {
    public static void main (String[] args){

        StringBuilder output = new StringBuilder("");

        for (int i = 1; i <= 48; i++){
            output.append("~");
        }
        output.append("\n");
        output.append("~");

    //etc.

        System.out.print(output);
    }
  }
}


所以我们知道每行多长时间,以及每个模式重复多少次,我们需要首先遍历每行,然后遍历每行字符。因此,骨架可能看起来像这样:

    final int NUMBER_OF_LINES = 4;
    final int LINE_LENGTH = 48;
    final int PATTERN_LENGTH = 3;
    final String[] LINE_NUMBER_PATTERNS = {"~~~","~+~","+~+","~~~"};


您还注意到我做了一个StringBuilder,它是一个字符串数组,每行包含一个模式。模式可以是任何字符串,任何长度。数组是按String[]索引的,因此我们将使用00来计数行,而不是31。 (注意,请使用4而不是<,因为我们要在第四行的索引<=处停止)

    for (int currentLine = 1; currentLine <= NUMBER_OF_LINES; currentLine++) {    
        for (int i = 1; i <= LINE_LENGTH; i += PATTERN_LENGTH) {
            // do some stuff here
        }
    }


它输出此结果,这几乎是正确的:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ + ~~ + ~~ + ~~ + ~~ + ~~ + ~~ + ~~ + ~~~~ + ~~ + ~~ + ~~ + ~~ + ~~ + ~~ + ~~ +〜+〜++〜++〜++〜++〜++〜++〜++〜++〜++〜++〜++〜++〜++〜++ 〜++〜+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~


因此,我们所缺少的是在退出内部循环之后,在每行结尾处换行3。一切都说完了,这就是我们所拥有的,并且运行良好。 (ideone.com上的演示)

    for (int currentLine = 0; currentLine < NUMBER_OF_LINES; currentLine++) {    
        for (int i = 1; i <= LINE_LENGTH; i += PATTERN_LENGTH) {
            output.append(LINE_NUMBER_PATTERNS[currentLine]);
        }
    }


评论


\ $ \ begingroup \ $
如何使用for(int currentLine = 0; currentLine \ $ \ endgroup \ $
–西蒙·福斯伯格
15年11月26日在19:08

\ $ \ begingroup \ $
当您拥有LINE_NUMBER_PATTERNS.length时,为什么还要使用NUMBER_OF_LINES?
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年11月26日在19:09

\ $ \ begingroup \ $
第一个很棒的答案。只有两个小巧的尼特。 (1)具有main函数的所有功能使维护或修改变得更加困难。返回stringbuilder的函数会更好。 (2)打印件是一个问题,因为它们将您要执行的操作与输出结果结合在一起。例如,第一和最后的图案/线是相同的,打印出中间结果使利用这一事实变得更加困难。
\ $ \ endgroup \ $
– jmoreno
15年11月28日在5:52

\ $ \ begingroup \ $
@jmoreno我完全同意,我希望你能理解,我以“初学者”的身份来尝试并匹配问题的精神,尽管我当然从中学到了:)
\ $ \ endgroup \ $
– ran
15年11月28日在7:47

\ $ \ begingroup \ $
@Phrancis:当然,我理解这一点,第二点是我的主要观点(因为轻微的挑剔可能会有一个“主要”观点),并且旨在解决您的“本质上没有错”。我认为混合计算值和输出结果有问题。我曾/将建议从“ While”中删除所有内容,然后删除“,”。
\ $ \ endgroup \ $
– jmoreno
15年11月28日在16:14

#4 楼

模式。它们不是很可爱吗?

我的代码中有两件事我不喜欢:


您正在使用很多重复的System.out.print调用,确实有一些开销(可能被认为是过早的优化,但我认为对此有所了解)。
您没有尽可能多地利用这些模式。

看一下您当前的代码:

System.out.print("~");
for (int i = 1; i <=15; i++){
    System.out.print("+~~");
}
System.out.println("+~");


System.out.print("+~");
for (int i = 1; i <= 15; i++){
    System.out.print("++~");
}
System.out.println("+");


您在这里做了一个非常有趣的拆分。如果看一下模式,您会发现第一个实际上只是~+~的一行,第二个实际上是+~+

虽然您的要求可能是使用嵌套的for循环,但我不会认为这是最好的方法,也是一种教育活动,我将解释如何以一种我认为更好的方法进行操作。

我将创建一个返回String的方法

public static String createPattern(String pattern, int length) {
    if (pattern.isEmpty()) {
        throw new IllegalArgumentException("Pattern cannot be empty");
    }
    StringBuilder builder = new StringBuilder(length);
    while (builder.length() < length) {
        int strLength = Math.min(pattern.length(), length - builder.length());
        builder.append(pattern, 0, strLength);
    }
    return builder.toString();
}


然后可以使用此方法:

String cleanLine = createPattern("~~~", 48);
System.out.println(cleanLine);
System.out.println(createPattern("~+~", 48));
System.out.println(createPattern("+~+", 48));
System.out.println(cleanLine);


或,您可以继续将您的模式视为一系列“ + ~~”(已向右移动了一步),这可以通过使用shift方法来完成。

public static String shift(String original, int stepsRight) {
    if (Math.abs(stepsRight) >= original.length()) {
        throw new IllegalArgumentException("Steps to shift, in either direction, must be less than string length");
    }
    if (stepsRight < 0) {
        // shifting left one step is the same as shifting it right (length - 1) steps.
        stepsRight += original.length();
    }
    int split = original.length() - stepsRight;
    return original.substring(split) + original.substring(0, split);
}


那么您可以执行以下操作:

String cleanLine = createPattern("~~~", 48);
System.out.println(cleanLine);
System.out.println(createPattern(shift("+~~", 1), 48));
System.out.println(createPattern(shift("++~", -1), 48));
System.out.println(cleanLine);


虽然我会使用非转换模式版本,但是如果您想制作转换模式,则可能会有用具有相同模式但已移动的多行。

评论


\ $ \ begingroup \ $
从某种意义上讲,存在嵌套,但是它不是练习所要求的嵌套的for循环。
\ $ \ endgroup \ $
– 200_success
15年11月26日在18:44



\ $ \ begingroup \ $
@ 200_success嵌套,嗯。。。显然没有对此要求给予太多关注。虽然可能对需求有轻微的误解,但我发现嵌套的for循环并不是最好的选择。在我的答案中添加了对此的评论。
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年11月26日在19:00

#5 楼

要生成更好的代码,需要做的就是学习识别代码中的模式,以查看是否要复制功能和代码。因此,当我查看您的代码时,我做的第一件事是问自己,某处是否存在重复项?答案是肯定的!


for (int i = 1; i <= 4; i++){
        for (int j = 1; j <= 12; j++){
            System.out.print("~");
        }
    }



这段代码既位于方法的开头,也位于方法的结尾。因此,您可以自己提取一种方法,而不必重复此方法!

其余的代码是“正确的”,因为它没有重复但仍然没有解决问题的好方法。任务是尝试考虑必须中断的模式。您应该已经看到第二行是序列~+~的副本,第三行是+~+的重复。我建议您考虑使用此解决方案来输出。

#6 楼

这是基于Java 8 Stream的方法... :)

使用@ 200_success的答案作为基础,我们知道重复的组可以只是"~~~", "~+~", "+~+", "~~~"(或跟随@CiaPan的评论,仅"~",因为我们通过比较长度来“循环”)。我们也知道我们需要重复48 / length of each String次。

private static final int LENGTH = 48;

// Usage
// Original
// Stream.of("~~~", "~+~", "+~+", "~~~")
Stream.of("~", "~+~", "+~+", "~")
        .map(v -> String.join("", Collections.nCopies(LENGTH / v.length(), v)))
        .forEach(System.out::println);



构造所需Stream元素的String
对于每个元素,map()通过join()-使用List创建的每个元素的nCopies(),将它们获得所需的结果。这使我们能够轻松地为给定数量的副本LENGTH / v.length()创建重复元素。
最后,将forEach()个元素传递给System.out.println(String)(在此处用作方法参考)。

当然,您可以简单地用硬编码的LENGTH / v.length()代替16(如果我们也使用"~~~"),但是我觉得这对于将来的变化有更大的灵活性(只需要注意舍弃LENGTH的非因数)。 br />
edit:将其转换为显式的for -loop结构,以更好地满足您的任务要求相对简单:

// Original
// for (String element : Arrays.asList("~~~", "~+~", "+~+", "~~~")) {
for (String element : Arrays.asList("~", "~+~", "+~+", "~")) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < LENGTH / element.length(); i++) {
        builder.append(element);
    }
    System.out.println(builder.toString());
}


类似的步骤正在执行以下操作:我们遍历相同的元素,通过使用StringBuilder为所需的副本数重复连接一个元素来“映射”每个元素,最后,我们打印每个toString()StringBuilder表示形式。 >

#7 楼

没有人真正回答过的这个问题的关键部分是;


编写嵌套的forloops


我的第一个想法是只是为了保持简单;

public class Exercises
{
    public static void main (String[] args)
    {
        for (int row = 0; row < 4; row++)
        {
            // Loop 0 - 15.
            // "column" 0 - "column" 15 are each 3 chars wide, for a total of 48 chars.
            for (int column = 0; column < 16; column++)
            {
                switch (row)
                {
                    // First and last row are the same, don't repeat code!
                    case 0:
                    case 3:
                        System.out.print("~~~");
                        break;
                    case 1:
                        System.out.print("~+~");
                        break;
                    case 2:
                        System.out.print("+~+");
                        break;
                }
            }
            // There's a newline at the end of each row, print it here, once the "column" printing is done.
            System.out.print("\n");
        }
    }
}


我不会说这太棒了,但它涵盖了您的老师/导师/自助指南正在尝试的要点make。


我们正在使用嵌套循环。这是关键。

我们正在使用正确命名的变量rowcolumn

还有其他事情可以做;


整个代码可以从main中取出。并采用自己的方法; WritePattern()。这使代码更具可扩展性。再说下一课,重复上述模式,您所要做的就是在方法中添加一个参数,并将该参数乘以4。
每个case语句也可以使用单独的方法,例如WriteRowPattern(int columnLimit, string pattern)其中47 / 16替换为columnLimit参数。同样,这使它更具可扩展性。进行此更改后,我们将从固定模式转换为所需的任何模式。


评论


\ $ \ begingroup \ $
我认为我们对“简单”有不同的定义吗?
\ $ \ endgroup \ $
– h.j.k.
15年11月27日在1:27

\ $ \ begingroup \ $
同意。我认为我不会教有关匿名函数,映射和“ foreach”循环声明的初学者。尤其是因为此问题的焦点嵌套在循环中。我认为很容易理解第一行是每一行,第二行是每一列。您可能可以调整switch语句并使它更清晰一些,仅此而已。如果您不了解特定的部分,很乐意进一步解释。
\ $ \ endgroup \ $
–特伦特
2015年11月27日5:29



\ $ \ begingroup \ $
嗯,您的解决方案的第一行和最后一行都缺少一个字符,因为您将\ n打印为第48个字符,而实际上应该是第49个字符。而且,显式地跳过第二行和第三行的列值17 ... 47的输出是...古怪的。恕我直言,不得不使用0 ... 4来遍历每一行,然后依靠开关来确定应该在每行打印什么,这也有点麻烦。在必须两次指定“ ~~~”与引入索引+开关之间,我认为前者既简短又易于实现。
\ $ \ endgroup \ $
– h.j.k.
15年11月27日在5:49

\ $ \ begingroup \ $
是的,好的,我没有Java编译器来检查输出,这很愚蠢,但我的工作很简单。回想一下您第一次学习Java的时间。在内部for循环中引用外部for循环的变量是您首先要教的一件事,我很确定这是OP的老师/辅导员/自助书所试图尝试的概念。
\ $ \ endgroup \ $
–特伦特
15年11月27日在6:07

\ $ \ begingroup \ $
@ h.j.k。是的,我可能会拉出换行符,然后将其放在内部for循环的末尾/外部,即,在每行写完“列”之后隐式地写一个换行符。我要的是快速/简单,而不是微观的优化/重构。
\ $ \ endgroup \ $
–特伦特
15年11月27日在6:36

#8 楼

我更喜欢其他答案,但是如果您真的被迫使用嵌套循环,我会去:

    for (int line = 0; line < 4; line++) {
        for (int i = 0; i < 16; i++) {
            int type = line % 3;
            if (type == 2) {
                System.err.print("+~+");
            } else if (type == 1) {
                System.err.print("~+~");
            } else {
                System.err.print("~~~");
            }
        }
        System.err.println();
    }


或者如果您认为三元运算符足够可读: >
for (int line = 0; line < 4; line++) {
    for (int i = 0; i < 16; i++) {
        int remainder = line % 3;
        System.err.print(remainder == 2 ? "+" : "~");
        System.err.print(remainder == 1 ? "+" : "~");
        System.err.print(remainder == 2 ? "+" : "~");
    }
    System.err.println();
}