是否可以在循环中的每个方法之后检查条件?如果是这样,如何?
Is it possible to check a condition after every method in a loop? If so, how?
就像我在标题中所说的那样,我在制作关于高中的角色扮演游戏中有一个循环。这是设置您的一天以按时间顺序执行各个序列的主循环。我的问题是我怎样才能检查 boolean
"beat" 或 boolean
"lost" (指的是游戏状态)是否已被触发为真在循环中的每个方法之后,但仍将这些方法保持在一个循环中。在我的 while 循环中嵌套 if 语句是唯一的方法吗?
while (!g.getBeat() || g.getLost())
{
g.wakeUp();
g.goToSchool();
g.beforeLunch();
g.lunchActivity();
g.afterLunch();
g.afterSchool();
g.home();
g.sleep();
}
您必须手动完成。为了帮助您少写一些代码,制作一个检查这两个条件的方法:
private boolean stopTheLoop() {
return g.getBeat() || g.getLost();
}
现在通过在每个方法之后进行检查将循环转换为无限循环:
while (true) {
g.wakeUp();
if (stopTheLoop()) break;
g.goToSchool();
if (stopTheLoop()) break;
g.beforeLunch();
if (stopTheLoop()) break;
...
}
您可以通过引入状态来使用 switch 语句:
int state = 0;
while (!g.getBeat() || g.getLost())
{
switch (state) {
case 0:
g.wakeUp();
break;
case 1:
g.goToSchool();
break;
case 2:
g.beforeLunch();
break;
case 3:
g.lunchActivity();
break;
case 4:
g.afterLunch();
break;
case 5:
g.afterSchool();
break;
case 6:
g.home();
break;
case 7:
g.sleep();
break;
default:
// some error handling, depending on your logic,
// or perhaps state = -1 to restart
}
state++;
}
如果您想像在示例中那样按照自己的方法保留每个步骤,您将无能为力...
如果满足停止循环的条件,将所有这些方法设为 return "true",则可以减少代码量...但是,如果您计划,这可能是不可能的在顺序上下文中使用这些方法。
if (!g.getBeat() || g.getLost()) do {
if (g.wakeUp()) break;
if (g.goToSchool()) break;
...
if (g.sleep()) break;
} while (true);
一个可能的技巧是让这些方法在满足停止条件时抛出异常。然后你会在循环外捕获那个异常。这样你就可以保存 if (...) break 语句。然而,这不是一个好的做法。
if (!g.getBeat() || g.getLost()) {
try {
do {
g.wakeUp();
g.goToSchool();
...
g.sleep();
} while (true);
} catch (ActivityLoopFinished ex) {
// nothing to do here
}
}
没有任何 "built-in" 方法可以做到这一点,但通过一些编码,一切皆有可能。
首先,无论你如何处理,我都会将结束条件包装到一个方法中,只是为了让事情更方便:
public class Game {
// method, members, etc...
public boolean isOver() {
return !getBeat() || getLost();
}
}
现在,想到的第一个选项是手动执行此操作:
while (!g.isOver()) {
g.wakeUp();
if (g.isOver()) {
break;
}
g.goToSchool();
if (g.isOver()) {
break;
}
// etc...
}
但这涉及到很多代码,并且不太优雅。
或许,一种更面向对象的方法是在处理程序中扭曲每个此类调用 class:
public abstract GameStageHandler (Game g) {
protected Game g;
public GameStageHandler (Game g) {
this.g = g;
}
/**
* Play a stage in the game
* @return Whether the game should go on or not after this stage
*/
public boolean play() {
performStage();
return !g.isOver();
}
public abstract void performStage();
}
并在游戏的每个阶段实施。例如。对于 wakeUp()
阶段,您将拥有:
public abstract WakeUpHandler (Game g) {
public WakeUpHandler (Game g) {
super(g);
}
@Override
public void performStage() {
g.wakeUp();
}
}
然后,在 main 方法中,您可以拥有一组这样的处理程序,并迭代它们:
List<GameStageHandler> handlers = ...;
while (!g.isOver()) {
for (GameStageHandler handler : handlers) {
if (!g.play()) {
break;
}
}
}
这可能超出了您的作业范围,因为您注意到 class 甚至 Runnable
还没有涵盖。不过,这是一个有趣的问题,挑战在于想出一种简洁而优雅的方式来表示它,同时尽可能避免重复。这是一个使用 Java8 和函数式编程技术的解决方案。
第一个洞察是每个游戏动作或步骤都可以表示为 lambda 表达式或方法引用。我假设您有 Game
class。每个这样的步骤都将一个 Game
实例作为参数(或接收者),因此可以输入为 "consumer" 个 Game
实例。因此我们可以将它们放入一个数据结构中:
List<Consumer<Game>> actions = Arrays.asList(
Game::wakeUp,
Game::goToSchool,
Game::beforeLunch,
Game::lunchActivity,
Game::afterLunch,
Game::afterSchool,
Game::home,
Game::sleep);
现在我们将它们放在数据结构中,我们可以遍历它们:
for (Consumer<Game> action : actions) {
action.accept(game);
}
当然,我们想在每个动作后检查游戏是否结束。假设您在 Game
class 上有一个方法 isOver
检查正确的终止条件。然后你可以这样做:
for (Consumer<Game> a : actions) {
a.accept(game);
if (game.isOver()) {
break;
}
}
一天的比赛只 运行 秒。大概你想 运行 游戏无限期地直到它达到它的终止条件。为此你需要一个外循环,并且终止检查必须跳出外循环:
outer:
while (true) {
for (Consumer<Game> a : actions) {
a.accept(game);
if (game.isOver()) {
break outer;
}
}
}
这本身就足够了:您有一个游戏动作列表,以及一个 运行 无限循环,在每个动作后检查终止条件。
但是等等,还有更多!这里仍然有相当多的样板文件,可以使用 Java 8 的一些流功能将其消除。考虑到可以使用 noneMatch
方法针对谓词测试流的每个元素。当其中一个谓词 returns 为真时,此方法终止。
因为每个动作都有类型Consumer<Game>
,我们需要一个小的辅助函数将每个动作变成一个谓词:
static Predicate<Consumer<Game>> stepAndCheck(Game game) {
return c -> { c.accept(game); return game.isOver(); };
}
现在我们可以运行一天的所有动作如下:
actions.stream().noneMatch(stepAndCheck(game))
无限期地 运行 游戏,我们只需将其包装在一个 while 循环中。因为 noneMatch
returns true
如果,正如它所说, none 谓词匹配,我们将其作为循环条件并将循环体留空:
while (actions.stream().noneMatch(stepAndCheck(game))) {
// nothing
}
这似乎不必要地晦涩难懂。事实上,对于像这样的玩具示例,它可能是。然而,对于更复杂的问题,像这样的技术是非常有价值的。
就像我在标题中所说的那样,我在制作关于高中的角色扮演游戏中有一个循环。这是设置您的一天以按时间顺序执行各个序列的主循环。我的问题是我怎样才能检查 boolean
"beat" 或 boolean
"lost" (指的是游戏状态)是否已被触发为真在循环中的每个方法之后,但仍将这些方法保持在一个循环中。在我的 while 循环中嵌套 if 语句是唯一的方法吗?
while (!g.getBeat() || g.getLost())
{
g.wakeUp();
g.goToSchool();
g.beforeLunch();
g.lunchActivity();
g.afterLunch();
g.afterSchool();
g.home();
g.sleep();
}
您必须手动完成。为了帮助您少写一些代码,制作一个检查这两个条件的方法:
private boolean stopTheLoop() {
return g.getBeat() || g.getLost();
}
现在通过在每个方法之后进行检查将循环转换为无限循环:
while (true) {
g.wakeUp();
if (stopTheLoop()) break;
g.goToSchool();
if (stopTheLoop()) break;
g.beforeLunch();
if (stopTheLoop()) break;
...
}
您可以通过引入状态来使用 switch 语句:
int state = 0;
while (!g.getBeat() || g.getLost())
{
switch (state) {
case 0:
g.wakeUp();
break;
case 1:
g.goToSchool();
break;
case 2:
g.beforeLunch();
break;
case 3:
g.lunchActivity();
break;
case 4:
g.afterLunch();
break;
case 5:
g.afterSchool();
break;
case 6:
g.home();
break;
case 7:
g.sleep();
break;
default:
// some error handling, depending on your logic,
// or perhaps state = -1 to restart
}
state++;
}
如果您想像在示例中那样按照自己的方法保留每个步骤,您将无能为力...
如果满足停止循环的条件,将所有这些方法设为 return "true",则可以减少代码量...但是,如果您计划,这可能是不可能的在顺序上下文中使用这些方法。
if (!g.getBeat() || g.getLost()) do {
if (g.wakeUp()) break;
if (g.goToSchool()) break;
...
if (g.sleep()) break;
} while (true);
一个可能的技巧是让这些方法在满足停止条件时抛出异常。然后你会在循环外捕获那个异常。这样你就可以保存 if (...) break 语句。然而,这不是一个好的做法。
if (!g.getBeat() || g.getLost()) {
try {
do {
g.wakeUp();
g.goToSchool();
...
g.sleep();
} while (true);
} catch (ActivityLoopFinished ex) {
// nothing to do here
}
}
没有任何 "built-in" 方法可以做到这一点,但通过一些编码,一切皆有可能。
首先,无论你如何处理,我都会将结束条件包装到一个方法中,只是为了让事情更方便:
public class Game {
// method, members, etc...
public boolean isOver() {
return !getBeat() || getLost();
}
}
现在,想到的第一个选项是手动执行此操作:
while (!g.isOver()) {
g.wakeUp();
if (g.isOver()) {
break;
}
g.goToSchool();
if (g.isOver()) {
break;
}
// etc...
}
但这涉及到很多代码,并且不太优雅。
或许,一种更面向对象的方法是在处理程序中扭曲每个此类调用 class:
public abstract GameStageHandler (Game g) {
protected Game g;
public GameStageHandler (Game g) {
this.g = g;
}
/**
* Play a stage in the game
* @return Whether the game should go on or not after this stage
*/
public boolean play() {
performStage();
return !g.isOver();
}
public abstract void performStage();
}
并在游戏的每个阶段实施。例如。对于 wakeUp()
阶段,您将拥有:
public abstract WakeUpHandler (Game g) {
public WakeUpHandler (Game g) {
super(g);
}
@Override
public void performStage() {
g.wakeUp();
}
}
然后,在 main 方法中,您可以拥有一组这样的处理程序,并迭代它们:
List<GameStageHandler> handlers = ...;
while (!g.isOver()) {
for (GameStageHandler handler : handlers) {
if (!g.play()) {
break;
}
}
}
这可能超出了您的作业范围,因为您注意到 class 甚至 Runnable
还没有涵盖。不过,这是一个有趣的问题,挑战在于想出一种简洁而优雅的方式来表示它,同时尽可能避免重复。这是一个使用 Java8 和函数式编程技术的解决方案。
第一个洞察是每个游戏动作或步骤都可以表示为 lambda 表达式或方法引用。我假设您有 Game
class。每个这样的步骤都将一个 Game
实例作为参数(或接收者),因此可以输入为 "consumer" 个 Game
实例。因此我们可以将它们放入一个数据结构中:
List<Consumer<Game>> actions = Arrays.asList(
Game::wakeUp,
Game::goToSchool,
Game::beforeLunch,
Game::lunchActivity,
Game::afterLunch,
Game::afterSchool,
Game::home,
Game::sleep);
现在我们将它们放在数据结构中,我们可以遍历它们:
for (Consumer<Game> action : actions) {
action.accept(game);
}
当然,我们想在每个动作后检查游戏是否结束。假设您在 Game
class 上有一个方法 isOver
检查正确的终止条件。然后你可以这样做:
for (Consumer<Game> a : actions) {
a.accept(game);
if (game.isOver()) {
break;
}
}
一天的比赛只 运行 秒。大概你想 运行 游戏无限期地直到它达到它的终止条件。为此你需要一个外循环,并且终止检查必须跳出外循环:
outer:
while (true) {
for (Consumer<Game> a : actions) {
a.accept(game);
if (game.isOver()) {
break outer;
}
}
}
这本身就足够了:您有一个游戏动作列表,以及一个 运行 无限循环,在每个动作后检查终止条件。
但是等等,还有更多!这里仍然有相当多的样板文件,可以使用 Java 8 的一些流功能将其消除。考虑到可以使用 noneMatch
方法针对谓词测试流的每个元素。当其中一个谓词 returns 为真时,此方法终止。
因为每个动作都有类型Consumer<Game>
,我们需要一个小的辅助函数将每个动作变成一个谓词:
static Predicate<Consumer<Game>> stepAndCheck(Game game) {
return c -> { c.accept(game); return game.isOver(); };
}
现在我们可以运行一天的所有动作如下:
actions.stream().noneMatch(stepAndCheck(game))
无限期地 运行 游戏,我们只需将其包装在一个 while 循环中。因为 noneMatch
returns true
如果,正如它所说, none 谓词匹配,我们将其作为循环条件并将循环体留空:
while (actions.stream().noneMatch(stepAndCheck(game))) {
// nothing
}
这似乎不必要地晦涩难懂。事实上,对于像这样的玩具示例,它可能是。然而,对于更复杂的问题,像这样的技术是非常有价值的。