likes
comments
collection
share

HW0 Java速成班

作者站长头像
站长
· 阅读数 15

HW目标

在这个作业中,我们将介绍基本的Java语法概念。虽然这个作业是可选的,您不需要提交答案,但对于没有Java经验的人来说,强烈建议完成。讲座不会明确涵盖这些材料,但是您应该理解它们。

本教程假定您具有至少一门编程语言的重要(一个学期)经验,并且仅旨在突出Java执行某些以前熟悉的操作的方式。

虽然我希望这个文档对于好奇和自我激励的学生来说足够自成体系,但是当提供建议的补充阅读时,您可能会发现它很有帮助。

随意浏览和以您感到舒适的任何速度阅读。可以跳过这个作业的某些部分。请用您最好的判断力。指示可能比必要的更冗长。

一个基础程序

在实验1中,我们将学习如何在计算机上运行Java代码。由于实验1尚未开始,因此我们将仅在此作业中使用基于浏览器的Java编译器。请前往此链接

您将看到您的第一个Java程序。这里有很多奇怪的东西,比如public class和public static void main(String [] args)。我们稍后会更详细地讨论它们,但是对于这个作业,您应该忽略所有这些神秘的垃圾。

单击“Forward>”链接两次。一旦执行int x = 5这一行,您将看到一个x出现在右侧的蓝色框中,其值为5。也许不出所料,这个语句将值5分配给变量x。

与其他编程语言(如Python、Scheme和MATLAB)不同,Java变量具有静态类型。我是说x只能存储整数。如果您尝试将数字5.3放入其中,代码将失败。

与这些其他语言不同,Java中的每个语句都必须后跟分号。分号非常重要,因为它告诉Java一个语句在哪里结束,另一个语句开始。

再次单击前进,您将看到x的值已更改为6。再单击一次前进,您将看到x在下面的程序输出框中使用相当冗长的命令名称System.out.println打印。是的,这确实是您在Java中打印的方式。相信我,它会变得更冗长。

通常,当您编写Java程序时,您将无法看到程序的内部(即没有列出所有变量的蓝色框)。但是,这个可视化工具是一种教学工具,可以使这种大脑扫描成为可能。

单击前进,直到程序完成执行。一切都应该表现得更或多或少像您预期的那样。如果有什么让您感到惊讶的事情,请在课程Piazza页面上发布HW0线程。

可选:尝试编辑代码并再次运行它。进行实验并查看在调整程序时会发生什么。如果您有有趣的观察结果或任何问题,请在Piazza上发布它们。也许尝试分配一个实数(例如3.3)并查看会发生什么(我保证您的计算机不会爆炸)。

条件

可选补充阅读: Shewchuk

基础条件

请打开这个程序:

public class ClassNameHere {
    public static void main(String[] args) {
        int x = 5;

        if (x < 10)
            x = x + 10;

        if (x < 10)
            x = x + 10;

        System.out.println(x);
    }
}

向前迈一步,直到程序完成并观察程序的流程。在Java中,if语句会检查您放在括号内的条件,如果结果为true(如果满足条件),则执行下面的下一条语句。

花括号(和条件语句)

在Java中,我们也可以针对单个条件执行多个语句。我们通过将语句放在花括号中来实现这一点,如下所示:

public class ConditionalsWithBlocks {
   public static void main(String[] args) {
      int x = 5;

      if (x < 10) {
         System.out.println("I shall increment x by 10.");
         x = x + 10;
      }

      if (x < 10) {
         System.out.println("I shall increment x by 10.");
         x = x + 10;
      }

      System.out.println(x);
   }
}

花括号在Java中非常重要!与Python不同,语句是由花括号而不是缩进分组的。如果您想知道这会出现什么问题,请尝试运行以下程序(或使用此链接),该程序应该打印x的绝对值。然后将x的值更改为正数。运行它并确保您理解为什么会出现问题。

public class PrintAbsoluteValue {
    public static void main(String[] args) {
        int x = -5;

        if (x < 0)
            System.out.println("I should negate X");
            x = -x;

        System.out.println(x);
    }
}

与Python不同,大多数空格(包括缩进)都与程序的功能无关。实际上,您可以用单个空格替换整个程序中的每个空格(给定分号是语句之间的分隔符),但这是一个可怕的想法,如果您编写以下有效的Java程序,我们会非常难过:

public class ClassNameHere { public static void main(String[] args) { int x = 5; if (x < 10) { System.out.println("I shall increment x by 10."); x = x + 10; } if (x < 10) { System.out.println("I shall increment x by 10."); x = x + 10; } System.out.println(x); } }

花括号的规范

花括号有两种常见的样式:

if (x > 5) {              if (x > 5)
    x = x + 5;            {
}                            x = x + 5;
                          }

哪种样式是“正确”的有点争议。在61B中,这两种样式都可以使用。请注意,在此示例中,我们将花括号包装在单个语句周围,这在Java中并不是必需的。在61B中,我们将始终使用花括号,即使我们只有一个语句要执行。这是为了避免错误。不要过于担心这些小细节,如果您做了不恰当的事情,自动样式检查器会警告您。

有关缩进样式的更多信息,请参见此维基页面。

Else

else关键字允许您指定如果条件的结果为false(如果条件未满足)应该发生的行为。例如,以下程序(或此程序):

int x = 9;
if (x - 3 > 8) {
    System.out.println("x - 3 is greater than 8");
} else {
    System.out.println("x - 3 is not greater than 8");
}

我们还可以链接else语句,例如以下程序(或此程序):

int dogSize = 20;
if (dogSize >= 50) {
    System.out.println("woof!");
} else if (dogSize >= 10) {
    System.out.println("bark!");
} else {
    System.out.println("yip!");
}

请注意,在上面的代码中,我使用了>=,它表示大于或等于。

While

可选补充阅读:Shewchuk

while关键字允许您在某些条件为true时重复执行一段代码块。例如,以下程序(或此程序):

尝试运行此程序,并观察会发生什么。请注意,一旦花括号内的代码完成,我们就会直接返回while条件。可选地,进行一些实验:尝试查看如果将bottles设置为-4会发生什么。还可以尝试查看如果删除bottles = bottles - 1;这一行会发生什么。

重要提示:您应该将程序视为按顺序逐行运行。如果条件在循环中间变为false,则代码不会简单地停止。因此,例如下面的代码(也在此处)将打印“-312瓶啤酒在墙上。”即使-312不大于0。

Doubles and Strings

在上面的例子中,我们的所有变量都是int类型。在Java中,有许多其他类型可供使用。其中两个示例是double和String。double存储实数的近似值,而String存储字符的字符串。以下程序(也在此处)模拟了阿喀琉斯和乌龟之间的比赛。阿喀琉斯的速度是乌龟的两倍,因此应该超过乌龟(乌龟有100个距离单位的领先优势)。

String a = "Achilles";
String t = "Tortoise";
double aPos = 0;
double tPos = 100;
double aSpeed = 20;
double tSpeed = 10;
double totalTime = 0;
while (aPos < tPos) { 
    System.out.println("At time: " + totalTime);
    System.out.println("    " + a + " is at position " + aPos);
    System.out.println("    " + t + " is at position " + tPos);

    double timeToReach = (tPos - aPos) / aSpeed;
    totalTime = totalTime + timeToReach;
    aPos = aPos + timeToReach * aSpeed;
    tPos = tPos + timeToReach * tSpeed;
}

创意练习1a:绘制三角形

终于有机会自己做点什么了。

您的目标是创建一个程序,打印以下图形。您的代码应该使用循环(即不应该只有五个打印语句,那样不好玩)。

您可以从头编写程序,也可以从此链接中复制和粘贴代码行。您可能会发现System.out.print是System.out.println的有用替代品。它们的区别在于System.out.print不包括自动换行符。

如果您选择复制和粘贴路线,请注意,行可以使用一次、多次或根本不使用。

运行代码并通过眼睛比较它与上面的程序是否正确。在下周的实验和作业中,我们将讨论更复杂的验证程序正确性的方法。

将代码保存在某个地方(例如通过电子邮件将其发送给自己),因为您很快将需要它。

定义结构体(又名方法)

以下四个代码片段在Python、MATLAB、Scheme和Java中都是等价的。每个函数定义了一个返回两个值中的最大值的函数,并打印5和15的最大值。

Python

def max(x, y):
    if (x > y):
        return x
    return y

print(max(5, 15))

MATLAB

function m = max(x, y)
    if (x > y)
        m = x
    else
        m = y
    end
end

disp(max(5, 15))

Scheme

(define max (lambda (x y) (if (> x y) x y)))
(display (max 5 15)) (newline)

Java

public static int max(int x, int y) {
    if (x > y) {
        return x;
    }
    return y;
}

public static void main(String[] args) {
    System.out.println(max(10, 15));
}

(程序链接 1)

在Java中,函数(方法)与变量一样,有特定的返回类型。max函数的返回类型为int(在函数名称之前的“int”表示)。此外,在Java中,函数称为方法,因此我将从此刻开始永远这样称呼它们。

我们将整个字符串public static int max(int x, int y)称为方法的签名,因为它列出了参数、返回类型、名称和任何修饰符。在这里,我们的修饰符是public和static,但是我们要等到几天后才能学习这些的含义。

对于本次作业,所有方法都将在其签名前面带有“public static”。现在请接受这一点。我们将在周五的讲座上进一步讨论这个问题。

创意练习1b:DrawTriangle

从我们的Java可视化器的默认程序开始(也可以在此处找到),创建一个具有一个附加方法的程序(除了打开可视化器时默认的main方法之外)。

将此新方法命名为drawTriangle,并将其返回类型设置为void(这意味着它根本不返回任何内容)。

drawTriangle方法应该带有一个名为N的参数,并打印出与练习1a中的三角形完全相同的三角形,但高度为N个星号,而不是5个星号。

编写DrawTriangle后,修改main函数,以N = 10调用DrawTriangle。

根据您的编程背景,您可能会发现这个任务非常具有挑战性。我们鼓励您与他人合作或在Piazza上发布。如果您只是困惑于从哪里开始,请参考此程序(也可以在此处找到),其中已经定义了DrawTriangle(但未实现详细信息)。

数组

补充阅读: Shewchuk

本次作业的最后一个新语法项是数组。数组类似于Scheme中的向量、Python中的列表和MATLAB中的数组。

以下四个程序在Python、MATLAB、Scheme和Java中声明了一个由整数4、7和10组成的新数组,然后打印出7。

Python

numbers = [4, 7, 10]
print(numbers[1])

MATLAB

numbers = [4 7 10]
disp(numbers(2))

Scheme

(define numbers #(4 7 10))
(display (vector-ref numbers 1)) (newline)

Java

int[] numbers = new int[3];
numbers[0] = 4;
numbers[1] = 7;
numbers[2] = 10;
System.out.println(numbers[1]);

或者用另一种(但不太通用的)简写:

Alternate Java

int[] numbers = new int[]{4, 7, 10};
System.out.println(numbers[1]);

你可以用.length方法获取数组长度,举个例子,下面的程序将显示3

int[] numbers = new int[]{4, 7, 10};
System.out.println(numbers.length);

Exercise 2

利用本次作业中所学的所有知识,现在您将创建一个签名为public static int max(int[] m)的函数,该函数返回int数组的最大值。您可以假设所有数字都大于或等于零。

修改下面的代码(也可以在此处找到),使其max方法按描述的方式工作。此外,修改main函数,以在给定数组上调用max方法并打印出其最大值(在本例中,应该打印22)。

For循环

考虑下面的函数,它对数组的元素求和。

public class ClassNameHere {
    /** Uses a while loop to sum a. */
    public static int whileSum(int[] a) {
      int i = 0; //initialization
      int sum = 0;
      while (i < a.length) { //termination
            sum = sum + a[i];
            i = i + 1; //increment
      }
      return sum;
    }
}

20世纪50年代的程序员们观察到,初始化变量,然后进行一个循环,该循环从检查终止条件开始,以增量操作结束,这种代码非常常见。因此,for循环应运而生。

下面的sum函数使用基本的for循环来完成与whileSum函数完全相同的工作。

public class ClassNameHere {
    /** Uses a basic for loop to sum a. */
    public static int sum(int[] a) {
      int sum = 0;
      for (int i = 0; i < a.length; i = i + 1) {
        sum = sum + a[i];
      }
      return sum;
    }
}

在Java中,for关键字具有以下语法:

for (initialization; termination; increment) {
    statement(s)
}

初始化、终止和增量必须以分号分隔。这三个中的每一个都可以包含多个逗号分隔的语句,例如:

for (int i = 0, j = 10; i < j; i += 1) {
    System.out.println(i + j);
}

逗号分隔的for循环应该谨慎使用。

练习3

重写练习2的解决方案,使其使用for循环。使用您的原始解决方案作为起始代码,或者如果您丢失了它,您可以使用这个)。

public class ClassNameHere {
    /** Returns the maximum value from m using a for loop. */
    public static int forMax(int[] m) {
        return 0;
    }
    public static void main(String[] args) {
       int[] numbers = new int[]{9, 2, 15, 2, 22, 10, 6};      
    }
}

跳出和继续

偶尔,您可能会发现使用break或continue关键字很有用。continue语句跳过当前循环迭代的其余部分,有效地直接跳到增量条件。

例如,下面的代码将从数组中打印每个字符串三次,但跳过任何包含“horse”的字符串。您可以在此链接上尝试它。

public class ContinueDemo {
    public static void main(String[] args) {
        String[] a = {"cat", "dog", "laser horse", "ketchup", "horse", "horbse"};

        for (int i = 0; i < a.length; i += 1) {
            if (a[i].contains("horse")) {
                continue;
            }
            for (int j = 0; j < 3; j += 1) {
                System.out.println(a[i]);
            }
        }
    }
}

相比之下,break关键字在调用时完全终止最内层的循环。例如,下面的代码将从数组中打印每个字符串三次,除了包含horse的字符串只打印一次。您可以在此链接上尝试它。

public class BreakDemo {
    public static void main(String[] args) {
        String[] a = {"cat", "dog", "laser horse", "ketchup", "horse", "horbse"};

        for (int i = 0; i < a.length; i += 1) {
            for (int j = 0; j < 3; j += 1) {
                System.out.println(a[i]);
                if (a[i].contains("horse")) {
                    break;
                }                
            }
        }
    }
}

break和continue也适用于while循环和do-while循环。如果您对do-while循环感到好奇,请参阅官方Java循环教程

练习4

这是一个特别具有挑战性的练习,但强烈推荐。

编写一个函数windowPosSum(int[] a, int n),它将每个元素a[i]替换为a[i]到a[i + n]的总和,但仅当a[i]为正值时。如果由于到达数组结尾而没有足够的值,则只计算我们拥有的值。

例如,假设我们使用数组a = {1, 2, -3, 4, 5, 4}和n = 3调用windowPosSum。在这种情况下,我们将:

  • 将a[0]替换为a[0] + a[1] + a[2] + a[3]。
  • 将a[1]替换为a[1] + a[2] + a[3] + a[4]。
  • 不对a[2]进行任何操作,因为它是负数。
  • 将a[3]替换为a[3] + a[4] + a[5]。
  • 将a[4]替换为a[4] + a[5]。
  • 不对a[5]进行任何操作,因为a[5]后面没有值。
  • 因此,在调用windowPosSum之后,结果将是{4, 8, -3, 13, 9, 4}。

另一个例子,如果我们使用数组a = {1, -1, -1, 10, 5, -1}和n = 2调用windowPosSum,则会得到{-1, -1, -1, 14, 4, -1}。

public class BreakContinue {
  public static void windowPosSum(int[] a, int n) {
    /** your code here */ 
  }

  public static void main(String[] args) {
    int[] a = {1, 2, -3, 4, 5, 4};
    int n = 3;
    windowPosSum(a, n);

    // Should print 4, 8, -3, 13, 9, 4
    System.out.println(java.util.Arrays.toString(a));
  }
}

起始代码可在此链接上获得。

提示1:使用两个for循环。

提示2:使用continue跳过负值。

提示3:使用break避免超过数组末尾。

增强for循环

Java还支持使用“增强型for循环”迭代数组。基本思想是,在许多情况下,我们实际上根本不关心索引。在这种情况下,我们使用一个特殊的语法,其中包含一个冒号,避免创建索引变量。

例如,在下面的代码中,我们与上面的BreakDemo完全相同。但是,在这种情况下,我们不创建索引i。相反,字符串s从a[0]开始,一直到a[a.length-1],每次都扮演a中每个字符串的身份。您可以在此链接上尝试此代码。

public class EnhancedForBreakDemo {
    public static void main(String[] args) {
        String[] a = {"cat", "dog", "laser horse", "ketchup", "horse", "horbse"};

        for (String s : a) {
            for (int j = 0; j < 3; j += 1) {
                System.out.println(s);
                if (s.contains("horse")) {
                    break;
                }                
            }
        }
    }
}