糟糕的代码
代码 1:
LOOP AT INTERNAL_TABLE.
IF SOMETHING.
GET_SOME_DATA USING INTERNAL_TABLE.
IF SOMETHING_ELSE.
GET_SOME_MORE_DATA USING INTERNAL_TABLE.
IF SOMETHING_ELSE_YET_AGAIN.
DO_SOMETHING.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
代码 2:
LOOP AT INTERNAL_TABLE WHERE SOMETHING = TRUE.
GET_SOME_DATA USING INTERNAL_TABLE.
CHECK SOMETHING_ELSE = TRUE.
GET_SOME_MORE_DATA USING INTERNAL_TABLE.
CHECK SOMETHING_ELSE_YET_AGAIN = TRUE.
DO_SOMETHING.
ENDLOOP.
这两个例程在功能上是相同的;只是第二个例程的行数较少,所以如果上下翻页的次数较少是重要的,而我实际上倾向于认为是重要的,那么你已经让普通的浏览者在一个屏幕上看到更多的例程了。
然而,GitHub 上的 SAP 整洁代码指南说不要在循环内使用 CHECK 语句。这是一个摇摆不定的例子--在循环中使用 IF 语句而不是 CHECK 语句会使代码变长......但它是否会使代码更清晰?
在前面的例子中,你会发现我减少了 IF/ENDIF 结构的数量。有深度嵌套的控制块(IF、CASE等)的编程术语是循环复杂性。
在 ABAP 编程指南一书中,其中一条规则是将 IF 语句等控制块的嵌套级别限制在 5 级。这是非常明智的建议--我曾一次又一次地看到巨大的 IF 语句块,深度嵌套,以至于当你做 "漂亮打印 "时,中间的部分会一直缩进到屏幕的最右边。
在这种情况下,我有一种难以忍受的冲动,想把 IF 和 ENDIF 之间的部分封装成自己的程序。这可以把那些简直无法理解的东西变成明显的东西。
代码 2 的 IF 语句嵌套的太深了。在我开始研究这段代码之前,每条 ENDIF 后面甚至没有任何注释。我加入了注释,作为简化代码的第一步;也就是说,在你改变它之前,你需要理解它。
在开发过程中,我们经常会遇到需要写双层循环的情况:
SORT gt_out ASCENDING by field1.
LOOP AT gt_out WHERE NOT field1 IS INITIAL.
LOOP AT gt_itab WHERE field1 = gt_out-field1.
APPEND gt_itab to gt_subtotal.
ENDLOOP.
ENDLOOP.
优化思路:
-
内表
gt_out的循环必须处理所有的行。没有什么可改进的,因为它将与表 1 中的行数成线性比例,也就是与 N1 成线性比例。很明显,上面的代码示例循环处理表gt_itab中的所有行,即 N2。嵌套循环的总规模为N1 * N2。如果 N1 和 N2 都随着处理的数据量 N 而增长,可以是位置数,那么嵌套循环的规模与 成正比,也就是说,它的规模是二次或非线性的。 -
因为
gt_itab中只有一行或几行与gt_out中的一行相对应。这些行必须被有效地找到,以便在 N2 上获得比线性更快地缩放行为。
LOOP AT gt_out INTO gs_out.
Efficient Operation to find the line
or lines on table gt_itab which correspond to wa_itab
ENDLOOP.
最快的访问类型是当被搜索的行可以被直接访问时,这使得这种读取类型与内部表的大小无关。这可以通过以下方式实现:
READ itab WITH indexREAD sort_tab' WITH indexREAD hash_tab WITH TABLE KEY ...
通过把其中一个 LOOP 转化为 READ TABLE 语句:
SORT: gt_out BY field1 field2,
gt_itab BY field1 field2.
LOOP AT gt_out.
READ TABLE gt_itab INTO wa_itab.
WITH KEY field1 = gt_out-field1
field2 = gt_out-field2
BINARY SEARCH.
IF sy-subrc = 0.
DO_SOMETHING.
ENDIF.
ENDLOOP.
为了使嵌套循环处理的完整性,有必要对作为内部操作的循环进行说明。如果内表 2 的几行能满足关键条件,就需要一个循环。循环可以通过以下方式实现
LOOP AT itab WHERE ...LOOP AT sort_tab WHEREREAD itab WITH KEY ... BINARY SEARCH, Save index, LOOP AT itab FROM index, Exit if condition is not fulfilled anymore.
为了更加精确,我们添加了以下代码:
SORT itab2 BY KEY.
LOOP AT gt_out INTO gs_out.
READ TABLE itab2 TRANSPORTING NO FIELDS WITH KEY key = gs_out-key
BINARY SEARCH.
tabix2 = sy-tabix.
LOOP AT itab2 INTO wa2 FROM tabix2.
IF ( wa2-key NE wa1-key ).
EXIT.
ENDIF.
" DO SOMETHING
ENDLOOP.
重要的是,表 2 上的 SORT 是在表 2 的 LOOP 之外,这样它就只被执行一次。二进制搜索可以有效地找到循环的起点,但同样重要的是,一旦条件不再满足,就立即离开循环。
对于标准表的 LOOP AT WHERE ,我们希望对表 2 的大小有一个线性依赖,因为它必须扫描整个表。其他两种方法应该显示出对数的依赖性。
特别是不太为人所知的标准表的变通方法可以帮助改进现有的程序,因为在这些程序中,表的类型已经不可能改变了。
以下是一些示例代码,演示如何优化 ABAP 中的双层 loop 循环:
- 避免在循环内部进行数据库访问:
* 不优化的代码
LOOP AT itab1 INTO wa1.
SELECT * FROM table2 INTO TABLE itab2 WHERE field = wa1-field.
LOOP AT itab2 INTO wa2.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
* 优化的代码
SELECT * FROM table2 INTO TABLE itab2.
LOOP AT itab1 INTO wa1.
LOOP AT itab2 INTO wa2 WHERE field = wa1-field.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
- 使用内部表:
* 不优化的代码
LOOP AT itab1 INTO wa1.
LOOP AT itab2 INTO wa2.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
* 优化的代码
DATA: itab3 TYPE TABLE OF ty_itab2.
APPEND LINES OF itab2 TO itab3.
LOOP AT itab1 INTO wa1.
LOOP AT itab3 INTO wa2 WHERE field = wa1-field.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
- 使用并行循环:
* 不优化的代码
LOOP AT itab1 INTO wa1.
LOOP AT itab2 INTO wa2.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
* 优化的代码
DATA: itab3 TYPE TABLE OF ty_itab2.
APPEND LINES OF itab2 TO itab3.
PARALLEL CURSOR WITH 8 FIELDS OF TABLE itab1 AS wa1.
LOOP AT itab3 INTO wa2 WHERE field = wa1-field.
" 在这里进行操作
ENDLOOP.
ENDPARALLEL.
- 避免在循环内部进行字符串操作:
* 不优化的代码
LOOP AT itab1 INTO wa1.
CONCATENATE wa1-field 'suffix' INTO lv_string.
" 在这里进行操作
ENDLOOP.
* 优化的代码
lv_suffix = 'suffix'.
LOOP AT itab1 INTO wa1.
CONCATENATE wa1-field lv_suffix INTO lv_string.
" 在这里进行操作
ENDLOOP.
- 使用 WHERE 条件:
* 不优化的代码
LOOP AT itab1 INTO wa1.
LOOP AT itab2 INTO wa2 WHERE field = wa1-field.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
* 优化的代码
SELECT * FROM table2 INTO TABLE itab2 WHERE field IN itab1-field.
LOOP AT itab1 INTO wa1.
LOOP AT itab2 INTO wa2 WHERE field = wa1-field.
" 在这里进行操作
ENDLOOP.
ENDLOOP.
- 使用二进制搜索:
* 不优化的代码
LOOP AT itab1 INTO wa1.
READ TABLE itab2 INTO wa2 WITH KEY field = wa1-field.
IF sy-subrc = 0.
" 在这里进行操作
ENDIF.
ENDLOOP.
* 优化的代码
SORT itab2 BY field.
LOOP AT itab1 INTO wa1.
READ TABLE itab2 INTO wa2 WITH KEY field = wa1-field BINARY SEARCH.
IF sy-subrc = 0.
" 在这里进行操作
ENDIF.
ENDLOOP.
- 避免在循环内部进行递归调用:
* 不优化的代码
FORM recursive_call.
LOOP AT itab1 INTO wa1.
CALL FUNCTION 'recursive_call'.
ENDLOOP.
ENDFORM.
* 优化的代码
FORM recursive_call.
LOOP AT itab1 INTO wa1.
" 在这里进行操作
ENDLOOP.
ENDFORM.
LOOP AT itab1 INTO wa1.
CALL FUNCTION 'recursive_call'.
ENDLOOP.
参考链接: