这将采用用户指定的宽度,并打印该宽度的菱形。它仅使用三个for循环,但是我可以进一步减少它吗?有没有更优雅的解决方案?

public class Diamond {

    static boolean cont = true;

    public static void main (String[] args) {
        Scanner input = new Scanner(System.in);
        while (cont) {
            System.out.print("Width: ");
            int width = input.nextInt();
            int lines = width;
            System.out.println();
            for (int line = 0; line < lines; line++) {
                for (int spaces = 0; spaces < Math.abs(line - (lines / 2)); spaces++) {
                    System.out.print(" ");
                }
                for (int marks = 0; marks < width - 2 * (Math.abs(line - (lines / 2))); marks++) {
                    System.out.print("x");
                }       
                System.out.println();
            }
            System.out.println();
        }
    }
}


#1 楼

我们可以做一些清理工作。


这是我要提取到自己的方法中的东西。处理通过main()方法获取用户输入,然后将其传递给drawDiamond()方法。
您的for循环分为在行,空格和
标记上迭代。我们可以简化为行和列,一次可以遍历每个单独的单元。这还将在最终方法中消除您的System.out.println()之一。

我们可以简化在哪里印刷钻石的数学运算。

if ((column == Math.abs(row - half)) || (column == (row + half)) || (column == (sqr - row + half - 1)))



最终方法:

void drawDiamond(int sqr)
{
    int half = sqr/2;
    for (int row=0; row<sqr; row++)
    {
        for (int column=0; column<sqr; column++)
        {
            if ((column == Math.abs(row - half)) || (column == (row + half)) || (column == (sqr - row + half - 1)))
            {
                System.out.print("*");
            }
            else System.out.print(" ");
        }
        System.out.println();
    }
}


#2 楼

您知道,很高兴看到代码能够执行所声明的内容,并且这是一个很好的简单任务,仍然需要花些时间来抓....但是我假设您是Java初学者。

基本知识

遍历一些基本内容...


方法外部已将cont变量声明为静态变量,但唯一的地方是使用的是方法内部。在这种情况下,应将声明移至main方法内。另外,没有任何改变该状态的程序,因此该程序可以运行,并且可以运行,这是可以的(初学者)。
您无需关闭input扫描仪。同样,这可能是因为该程序永远不会完成,但是Java7中有很多不错的方法可以确保它整洁地发生,并且无需花费太多精力。
您应该验证用户输入。如果用户输入负整数,则实际上可以(程序不执行任何操作)。更令人担忧的是,如果用户输入2000000000,则应设置一个上限。您可以将此形状称为“钻石”,但实际上它是一个正方形。宽度和高度是相同数量的字符。您只需要一个变量,lineswidth,而不是两个都使用。
混淆了您同时拥有linelines变量。如果您改用lines,则不需要width,因此请消除它(也是因为用户提示是“ Width:”)。

好,那是一些相对简单的东西。使用上面的建议来弄乱代码,我会得到:

public static void main (String[] args) {
    boolean cont = true;

    try (Scanner input = new Scanner(System.in)) {
        while (cont) {
            System.out.print("Width: ");
            int width = input.nextInt();
            System.out.println();

            if (width > 100) {
                System.out.println("Width too wide, reducing to 100");
                width = 100;
            }

            for (int line = 0; line < width; line++) {
                for (int spaces = 0; spaces < Math.abs(line - (width / 2)); spaces++) {
                    System.out.print(" ");
                }
                for (int marks = 0; marks < width - 2 * (Math.abs(line - (width / 2))); marks++) {
                    System.out.print("x");
                }       
                System.out.println();
            }
            System.out.println();
        }
    }
}


算法

好的,现在,一些算法问题:



System.out.print(...)println变体实际上确实很慢。从内部循环调用它们是性能的实际问题,并且是学习的坏习惯。这些方法锁定控制台输出,并且对其他线程也不好。在可能的情况下,应始终将字符打印批处理成更大的语句。
有时,拿走东西比添加东西容易....(含糊的提示)。

通过一些技巧,我们可以解决循环中的许多复杂性。这是一个建议:


建立两个字符串,一个为空格,另一个为“ x”字符。每个参数至少应与我们所需的最长值一样长。
遍历行并使用上述两个字符串中的每个字符串的一部分。

逻辑与您的逻辑完全相同除了我有很大的事情我会使用一部分,而你却一点一点地建立起来。重要的部分是Math.abs(...)语句与以前的版本相同....它们是我们构建的值的限制。

这是一种实现方法:

public static void main (String[] args) {
    boolean cont = true;

    try (Scanner input = new Scanner(System.in)) {
        while (cont) {
            System.out.print("Width: ");
            int width = input.nextInt();
            System.out.println();

            if (width > 100) {
                System.out.println("Width too wide, reducing to 100");
                width = 100;
            }

            char[] spaces = new char[width / 2];
            char[] exes = new char[width];
            Arrays.fill(spaces, ' '); // now an array of spaces
            Arrays.fill(exes, 'x'); // now an array of 'x'

            for (int line = 0; line < width; line++) {
                String pad = new String(spaces, 0, Math.abs(line - (width / 2)));
                String fill = new String(exes, 0, width - 2 * (Math.abs(line - (width / 2))));
                System.out.println(pad + fill);
            }
            System.out.println();
        }
    }
}


这只是您要考虑的问题.....

评论


\ $ \ begingroup \ $
同意第一句话。多年前,我花了2个小时在公共汽车上解决了抽奖钻石问题,而现在我自己解决了这个问题,我对此感到非常高兴。
\ $ \ endgroup \ $
–艾肯·雅西(AycanYaşıt)
2014年1月30日8:39



\ $ \ begingroup \ $
称其为“钻石”并非不正确。实际上,正方形像钻石一样新颖而令人兴奋!
\ $ \ endgroup \ $
– 200_success
2014年1月30日12:43

\ $ \ begingroup \ $
如果将char数组替换为最大长度的String文字,则可以避免Arrays.fill()调用,而只是采用子字符串,这样可以保存新String(char [])的基础数组副本。
\ $ \ endgroup \ $
– Bowmore
2014年1月31日0:00

#3 楼

人们已经对语法,程序结构等进行了许多重要的研究。但是,我认为您可以使循环更简单易懂:

int halfheight = (width + 1) / 2;
int spaces = halfheight;
int exes = width - 2 * spaces;

for (int i = 0; i < halfheight; i++)
{
    spaces--;
    exes += 2;

    // You could use the approaches suggested by other folk here instead of inner loops
    for (int s = 0; s < spaces; s++)
        System.out.print(" ");
    for (int x = 0; x < exes; x++)
        System.out.print("X");
    System.out.print("\n");
}

for (int i = 0; i < halfheight - 1; i++)
{
    spaces++;
    exes -= 2;

    for (int s = 0; s < spaces; s++)
        System.out.print(" ");
    for (int x = 0; x < exes; x++)
        System.out.print("X");
    System.out.print("\n");
}


(我相信这可以处理与您完全相同的奇数和偶数宽度,只是在偶数宽度的情况下它不会输出初始空白行。我认为这是人工制品,而不是规范的一部分。)

我唯一要做的“算术”是在初始化中,然后循环使用无需任何计算即可完全清除的数量,并以完全显而易见的方式更改每次迭代中的exe和空格数。用abs和整数除法进行的各种计算似乎并不复杂,但是,当您调试或尝试对代码进行稍微修改时,您会浪费时间思考不同的情况(正,负,奇,偶,初次迭代,最后一次迭代),可能会发现自己只是在测试不同的值somethingsomething + 1something - 1等,而不是能够在边缘情况下看到每个变量的精确交互。

请注意,我有6个for循环,并且代码比您或其他人的相应部分长得多。并不是减少这些使代码清晰。事实是很容易掌握每个函数所做的事情,而无需引用有助于维护者的循环之外的(很多)东西。恕我直言(我知道有些人会不同意),这里的重复很少且对称地使用,因为它比我将其排除在外要优雅得多。

#4 楼

将您的所有代码都放入main()是不好的做法。在这里,您还有另一个问题,即main()要做三件事:提示输入,打印菱形和循环。 (顺便说一句,您提供了从无穷循环中退出的无错误方法。)将这些任务混合到一个函数中将使它变得不可能,例如,无法以其他方式重用您的菱形印刷例程(例如使连续的垂直菱形字符串(由几个菱形组成)。

@ syb0rg提供了一种从打印例程中分离输入例程的方法。我会进一步建议在Java中建立面向对象的接口是一种好习惯。这是一种方法:

private static int promptWidth(Scanner input) {
    System.out.print("Width: ");
    return input.hasNextInt() ? input.nextInt() : 0;
}

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    int width;
    // Exit cleanly on EOF, or if anything other than a positive
    // integer is entered.
    while ((width = promptWidth(input)) > 0) {
        System.out.println();
        new Diamond(width).draw(System.out);
        System.out.println();
    }
    input.close();
}


换句话说,Diamond知道如何将自己吸引到System.out

这是另一种方法:

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    int width;
    // Exit cleanly on EOF, or if anything other than a positive
    // integer is entered.
    while ((width = promptWidth(input)) > 0) {
        int width = input.nextInt();
        System.out.println();
        System.out.println(new Diamond(width));
        System.out.println();
    }
    input.close();
}


,这依赖于Diamond.toString()方法,您必须使用StringBuilder来实现。

#5 楼

您确实可以在一个循环中完成它。
由于我不会讲Java,所以我将使用C#语法。

public static void Main()
{
    string valueString;
    int width;
    do
    {
        Console.Write("Width:");
        valueString = Console.ReadLine();
    } while (!int.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out width) && width > 0 && width <= 100);
    int half = (int)((double)width/2+0.5);
    string pattern = new string(' ',width)+new string('*',width);
    for (int row = 1; row <= width; row++)
    { 
        int spaces = width-Math.Abs(half - row);
        Console.WriteLine(pattern.Substring(spaces, spaces));
    }
}



为了您的方便而翻译成Java ....

public static void main (String[] args) {

    try (Scanner scanner = new Scanner(System.in)) {
        int width;
        do
        {
            System.out.print("Width:");
            width = scanner.nextInt();
        } while (width < 0 || width > 100);

        char[] blanks = new char[width];
        char[] exes = new char[width];
        Arrays.fill(blanks, ' ');
        Arrays.fill(exes, 'x');
        String pattern = new String(blanks) + new String(exes);

        int half = (int)((double)width / 2 + 0.5);
        for (int row = 1; row <= width; row++)
        {
            int spaces = width - Math.abs(half - row);
            // Java substring has arguments (first, last), not (first, length).
            System.out.println(pattern.substring(spaces, spaces + spaces));
        }                

        System.out.println();
    }
}


评论


\ $ \ begingroup \ $
是的,我已经对此进行了测试,输出就是您所期望的,请自己尝试。 space的值是在循环内部计算的,并且每次迭代都不同。诀窍是构造一个包含X个前导空格和X个菱形字符的字符串,在每个循环中,我们都从字符串中取出不同的部分。
\ $ \ endgroup \ $
– Marc Selis
2014年1月30日15:26

\ $ \ begingroup \ $
哦....(...一分钱掉了...)这很聪明(我已经在调试中逐步执行了您的代码。)。 ;-) 感谢那。如果可以的话,将+2。
\ $ \ endgroup \ $
–rolfl
2014年1月30日15:37