现有一个数据清洗任务。有多层循环,如下伪代码。 我的问题是:当 cleanedCount 达到 1 百万条数据时,要停止整个清洗任务。难道要一层一层 return 出去么?
class CleanData{
  // 已清洗数据量
  cleanedCount ;
  // 迁移任务
  method cleanByTask() {
    activityIdList.foreach{activityId -> cleanByActivity(activityId)}
  }
  // 按活动
  method cleanByActivity(activityId) {
    tables.foreach{table -> cleanByTable(table)}
  }
  // 按表
  method cleanByTable(table) {
    timeSplits.foreach{timeSplit -> cleanByTimeSplit(timeSplit)}
  }
  
  // 按时间切片
  method cleanByTimeSplit(timeSplit) {
    每 500 条.foreach{500 条 -> cleanByCount(500 条)}
  }
  // 按数量
  method cleanByCount(500 条) {
    cleanedCount += 500
    if (cleanedCount >= 1000000) {
      reurn;
    }
  }
}
|  |      1sockpuppet9527      2023-11-01 10:52:21 +08:00 本身,引入一个状态机?或在 caller 那边做个状态? | 
|  |      2chendy      2023-11-01 10:53:01 +08:00 代码目测没啥问题 一层一层 reture 也没啥问题 难道楼主需要 System.exit(0) ? | 
|  |      3elliottzhao87      2023-11-01 10:53:11 +08:00 在外部放一个变量,检测变量直接在外层跳出呗? | 
|  |      4irrigate2554      2023-11-01 10:55:34 +08:00  1 抛出个 Runtime 异常,最外层捕获处理,当然这个是旁门左道,正常还是多层 return 吧。 | 
|      5xwayway      2023-11-01 11:01:22 +08:00  15 当然是 goto 啦,想去哪儿去哪儿 | 
|  |      7virusdefender      2023-11-01 11:04:56 +08:00  1 抛出特定的异常 | 
|  |      8darkengine      2023-11-01 11:07:14 +08:00 Java 的不知道啊。这个逻辑在 JS/TS 有问题,即使总数大于 1000000 也只是退出一个 foreach 代码块,没跑完的还是会空跑。 | 
|  |      10broken123      2023-11-01 11:08:45 +08:00 直接问 chatgpt 即可 | 
|  |      11lsk569937453      2023-11-01 11:11:05 +08:00 用 stream.flatMap.limit 完美解决 | 
|  |      12darkengine      2023-11-01 11:15:53 +08:00 而且 foreach 空跑还会进入 cleanByCount ,现在的代码会导致 cleanedCount 不准确 method cleanByCount(500 条) { cleanedCount += 500 if (cleanedCount >= 1000000) { reurn; } } 改成 method cleanByCount(500 条) { if (cleanedCount >= 1000000) { reurn; } cleanedCount += 500 } | 
|  |      13pengtdyd      2023-11-01 11:28:37 +08:00 那当然是 kill -9 啦 | 
|      14cailinunix      2023-11-01 11:29:00 +08:00 尝试扁平化你的数据,把多层循环拍平成一个迭代器,然后就可以随便跳出了 | 
|  |      15Aresxue      2023-11-01 11:33:32 +08:00 所以说 goto 部分场景下还是有价值的。 针对这个场景用流也是个不错的方案。 | 
|  |      16nthin0      2023-11-01 11:36:04 +08:00  1 我选择抛特定异常。。一层层 return 要加的判断比较多,有点丑陋 | 
|  |      17williamx      2023-11-01 11:37:59 +08:00  1 绝大多数情况下,你这个是伪需求。每一层都有退出条件,退出时需要收尾,所以最内层退出后会自动一层层退出。 如果代码写得有漏洞 / 某些层条件没有配置好不能修改或者不想修改 / 想走特殊的逻辑而不是原来正常的逻辑,那就是异常情况,使用异常处理。 | 
|      18yazinnnn0      2023-11-01 11:39:30 +08:00  1 java 有 goto 关键字, 但是这个关键字没有作用 有 break label 的语法, 但是你拆成多个函数就没办法用了 还是抛异常吧 | 
|      19GeruzoniAnsasu      2023-11-01 11:45:24 +08:00  1 #7 +1: method exceptionCaptured() { try {cleanByTask()} catch(CountLimitExceeded){} } #4 > 抛出个 Runtime 异常,最外层捕获处理,当然这个是旁门左道,正常还是多层 return 吧。 不是旁门左道,这种不就是 recoverable exceptions, 抛异常是对的 | 
|      20Leviathann      2023-11-01 13:40:50 +08:00 用 monad 的 flatMap | 
|      21nodejsexpress      2023-11-01 13:47:02 +08:00 不用 for, 用 while, 多几个条件 and 一起就好了. | 
|  |      22adoal      2023-11-01 13:52:08 +08:00 惰性求值,摊平成迭代器 | 
|  |      23Chemist      2023-11-01 14:06:57 +08:00 via iPhone do for each if something break while false | 
|      24BIGBIG OP 结案啦:1. 抛出指定异常; 2. 然后捕获异常。优雅永不过时 感谢各位巨佬。 帖子下沉啦 | 
|  |      25iosyyy      2023-11-01 15:12:08 +08:00 尽量减少循环 | 
|      26baoshijiagong      2023-11-01 16:50:44 +08:00  1 要看情况,如果“cleanedCount 达到 1 百万条数据” 是异常情况,那么用抛异常,属优雅;如果是正常流程,那么只是形式上的优雅,逻辑上不算。 后者可以用方法函数,将多层的 cleanByXXX 的实现抽象成同一个抽象方法,在这个抽象方法判断 cleanedCount 即可。 新建抽象方法: public <P, C> void clean(P parentId, Function<P, List<C>> getChildList, Consumer<C> cleanChild) { if (cleanedCount < 10_000_000) { List<C> childList = getChildList.apply(parentId); childList.forEach(cleanChild); } } 比如 cleanByTask 改成: public void cleanByTask() { clean(null, p -> { // getActivityIdList return new ArrayList<>(); }, this::cleanByActivity); } | 
|      27gg1025      2023-11-01 17:37:03 +08:00 别问,问就是 goto | 
|  |      28KaGaMiKun      2023-11-01 17:40:28 +08:00 第一反应是实现迭代器 但这迭代器使用场景很少,仅仅可能为了这处省个返回才写的,顿时感觉还是不如老实返回 | 
|      29billccn      2023-11-02 07:10:24 +08:00  1 抛异常就是正解,一些函数式编程语言(比如 OCaml )抛异常是从内层退出循环的唯一的方式,这些语言是研究计编程理论的人喜欢用的,他们都没觉得是抛异常是旁门左道。 有一些老程序员会说尽量不要抛异常,是因为收集堆栈信息会比较慢,但是很多编程语言现在都对异常做了优化。比如 Java 虚拟机能识别这种用于控制程序执行,而不是用于报告错误的异常,抛出这种异常的时候里面就不会有堆栈信息,与创建一个普通对象无异。 | 
|  |      30mmdsun      2023-11-03 00:35:58 +08:00  1 fillInStackTrace 抛异常,对性能影响很小。 public final class StopException extends RuntimeException { public static final StopException INSTANCE = new StopException(); @Override public synchronized Throwable fillInStackTrace() { return this; } } | 
|  |      31leee41      2024-03-14 14:26:42 +08:00 goto label 你看反编译出来的 class 就能经常看到了,这个真有用,你这个场景完美 cover | 
|  |      32leee41      2024-03-14 14:28:00 +08:00 代码补充 ``` outer: // 这是一个标签 for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (i * j > 10) { System.out.println("Breaking from nested loop"); break outer; // 跳出标签指定的循环 } } } System.out.println("Exited loop"); ``` |