This is a test message to test the length of the message box.
Login
ABAP Deep Dive FOR
Erstellt von Software-Heroes

ABAP Deep Dive - FOR (Schleifen)

404

Schauen wir uns einmal die FOR Schleife etwas näher an. Wie funktioniert sie? Was muss ich beachten und was kann ich damit tun?

Werbung

Mit der Einführung neuer Statements in die ABAP Sprache, wurde auch ein Zusatz für den Loop entwickelt. Mit FOR kann man eine Schleife in einer Zeile abbilden, um so über Daten zu iterieren. In den folgenden Abschnitten schauen wir uns einmal die Ausprägungen dieser Form näher an.

 

Einleitung

Eines muss man Vorweg sagen, die FOR Schleife ist kein Ersatz für den LOOP. Sie funktioniert nur innerhalb der Statements REDUCE, NEW und VALUE, sogenannten Konstruktor Ausdrücken. In die FOR Schleife kann man auch nicht nach belieben Statements aufnehmen, wie es im LOOP der Fall ist.

 

Vorbereitung

Bevor wir mit den Beispielen starten, legen wir uns noch entsprechende Strukturen an, mit denen wir in den Beispielen arbeiten werden. Die Strukturen sind wie folgt definiert:

TYPES:
  td_key TYPE c LENGTH 5,

  BEGIN OF ts_simple_data,
    tkey   TYPE td_key,
    text   TYPE string,
    number TYPE i,
    date   TYPE d,
  END OF ts_simple_data,
  tt_simple_data TYPE STANDARD TABLE OF ts_simple_data WITH EMPTY KEY,

  BEGIN OF ts_easy_data,
    tkey   TYPE td_key,
    text   TYPE string,
    number TYPE i,
  END OF ts_easy_data,
  tt_easy_data TYPE STANDARD TABLE OF ts_easy_data WITH EMPTY KEY,

  tt_numbers   TYPE STANDARD TABLE OF i WITH EMPTY KEY.

 

Dazu implementieren wir noch eine Methode, die uns einen Satz Testdaten zurück gibt:

METHOD generate_data.
  rt_result = VALUE #(
    ( tkey = 'A' text = `Banana` number = 14 date = '20230101' )
    ( tkey = 'B' text = `Tomato` number = 12 date = '20230201' )
    ( tkey = 'C' text = `Apple` number = 23 date = '20231201' )
    ( tkey = 'E' text = `Strawberry` number = 31 date = '20230101' )
    ( tkey = 'F' text = `Salad` number = 20 date = '20230601' )
  ).
ENDMETHOD.

 

Zählvariable

Aus anderen Programmiersprachen wirst du sicherlich die Zählschleife kennen. Eine Schleife der man mitgibt, von wo nach wo gezählt werden soll und dann mit der entsprechenden Erhöhung der Zahl. Bauen wir dazu eine leere Tabelle mit Zahlen auf. Im VALUE Ausdruck implementieren wir nun die Schleife und beginnen mit FOR, dann der Variable und dem Startwert und bis wohin gezählt werden soll. Zum Abschluss kommt in die Klammern die zu erzeugende Struktur:

DATA(lt_numbers_until) = VALUE tt_numbers(
  FOR i = 1 UNTIL i >= 10 ( i )
).

 

Neben dem Statement UNTIL gibt es auch WHILE, je nachdem, was man für seinen Einsatz bevorzugt. Ein Beispiel für diese Art von Schleife sieht ähnlich aus:

DATA(lt_numbers_while) = VALUE tt_numbers(
  FOR i = 1 WHILE i <= 10 ( i )
).

 

Ist nichts angegeben, dann wird die Variable um +1 erhöht, hier gibt es aber auch noch die Möglichkeit die Zählweise anzupassen hinter THEN. Im folgenden Beispiel erhöhen wir den Zähler um +3:

DATA(lt_numbers_then) = VALUE tt_numbers(
  FOR i = 1 THEN i + 3 WHILE i <= 10 ( i )
).

 

Die Ausgabe aus den drei Schleifen sieht wie folgt aus:

 

Datentyp

In den oberen Beispielen haben wir bisher immer mit Integer gezählt, es sind aber auch andere Datentypen wie Datum (D), Zeit (T) oder Numerisch (N) möglich. Als nächstes einmal ein Beispiel für eine Zählvariable mit Datum, dabei übernehmen wir nur jeden ungeraden Tag in unsere neue Tabelle:

DATA(lt_date_variable) = VALUE tt_simple_data(
  FOR i = CONV d( '20230101' ) THEN i + 2 WHILE i <= '20230201' ( date = i )
).

 

Um den richtigen Datentypen zu erzeugen, konvertieren wir den Wert in die Variable, die Berechnung kommt mit dem Datumsfeld im Anschluss klar. Das Ergebnis der Tabelle sieht dann wie folgt aus:

 

FOREACH

In anderen Programmiersprachen wird die Schleife als FOREACH bezeichnet. Bei dieser Schleife geht es darum alle Datensätze einer Tabelle zu verarbeiten. Dies könnte man mit einem COUNT und einer Zählschleife machen, doch wieso kompliziert machen, wenn es auch einfach geht. Als erstes einmal benötigen wir eine Basistabelle:

DATA(lt_base) = generate_data( ).

 

Im nächsten Schritt bauen wir zwei Schleifen auf, eine Schleife die eine einfache Transformation mit CORRESPONDING macht und eine zweite Schleife, die die Felder übernimmt, aber einige Felder anders behandelt:

DATA(lt_corresponding) = VALUE tt_easy_data(
  FOR ls_base IN lt_base ( CORRESPONDING #( ls_base ) )
).

DATA(lt_simple_mapping) = VALUE tt_easy_data(
  FOR ls_base IN lt_base ( tkey = ls_base-tkey text = ls_base-text number = 1 )
).

 

Das Ergebnis der beiden Schleifen sieht wie folgt aus:

 

WHERE

Ähnlich wie beim LOOP haben wir auch die Möglichkeit die Daten einzuschränken und anhand der Inhalte abzugrenzen. Im folgenden Beispiel schränken wir die Daten über das Datum ein, zusätzlich haben wir noch die Möglichkeit den Index der aktuellen Zeile in der Schleife zur Verfügung zu stellen:

DATA(lt_where) = VALUE tt_easy_data(
  FOR ls_base IN lt_base INDEX INTO ld_idx WHERE ( date > '20230101' )
  ( tkey = ls_base-tkey text = ls_base-text number = ld_idx )
).

 

Die Zeile übernehmen wir in den neuen Datentypen und setzen das Feld "number" auf den Index der Quellzeile, so können wir den Datensatz später noch einmal wiederfinden. Das Ergebnis nach der Schleife sieht nun wir folgt aus:

 

REDUCE

Als letztes Beispiel schauen wir uns einmal das Statement REDUCE an, welches Inhalte reduziert bzw. zusammenfassen soll. Auch hier ist es möglich die FOR Schleife einzusetzen. Dazu schauen wir uns einmal drei verschiedene Szenarien an und wie man sie einsetzen kann.

Zuerst einmal verwenden wir eine Zählschleife und summieren das Quadrat der Zählvariable zusammen. Dabei definieren wir mit INIT eine lokale Variable, hier kannst du verschiedene Variablen zum Rechnen und Speichern anlegen, entscheidend ist allerdings die erste Variable, deren Datentyp auf das Ergebnis mappbar sein sollte:

DATA(ld_number) = REDUCE i(
  INIT num = 0
  FOR i = 1 THEN i + 3 WHILE i <= 10
  NEXT num = num + ( i * i )
).

 

Im zweiten Beispiel summieren wir alle Zahlen in unserer Basistabelle und geben das Ergebnis aus dem REDUCE zurück:

DATA(ld_sum) = REDUCE i(
  INIT sum = 0
  FOR ls_base IN lt_base
  NEXT sum = sum + ls_base-number
).

 

Als letztes Beispiel rechnen wir einmal nichts zusammen, sondern setzen einen Text aus den Inhalten von Spalten aus der Tabelle zusammen. Dazu ist es wichtig, dass die Variable mit dem String Literal initialisiert wird, da sie sonst als CHAR1 definiert wird und die Verkettung nicht funktioniert:

DATA(ld_keys) = REDUCE string(
  INIT keys = ``
  FOR ls_base IN lt_base
  NEXT keys = keys && ls_base-tkey
).

 

Zum Abschluss noch einmal die Ausgabe aus den drei Beispielen und wie sie in der Console aussehen würden:

 

Vollständiges Beispiel

Wie immer noch zum Abschluss die vollständige ausführbare Klasse, die wir für die Beispiele verwendet haben:

CLASS zcl_bs_demo_for DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

    TYPES:
      td_key TYPE c LENGTH 5,

      BEGIN OF ts_simple_data,
        tkey   TYPE td_key,
        text   TYPE string,
        number TYPE i,
        date   TYPE d,
      END OF ts_simple_data,
      tt_simple_data TYPE STANDARD TABLE OF ts_simple_data WITH EMPTY KEY,

      BEGIN OF ts_easy_data,
        tkey   TYPE td_key,
        text   TYPE string,
        number TYPE i,
      END OF ts_easy_data,
      tt_easy_data TYPE STANDARD TABLE OF ts_easy_data WITH EMPTY KEY,

      tt_numbers   TYPE STANDARD TABLE OF i WITH EMPTY KEY.

  PROTECTED SECTION.
  PRIVATE SECTION.
    METHODS:
      generate_data
        RETURNING VALUE(rt_result) TYPE tt_simple_data,

      simple_for_with_counter
        IMPORTING
          io_out TYPE REF TO if_oo_adt_classrun_out,

      simple_for_with_mapping
        IMPORTING
          io_out TYPE REF TO if_oo_adt_classrun_out,

      for_with_reduce
        IMPORTING
          io_out TYPE REF TO if_oo_adt_classrun_out,

      for_with_where_condition
        IMPORTING
          io_out TYPE REF TO if_oo_adt_classrun_out,
      simple_for_with_date
        IMPORTING
          io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.


CLASS zcl_bs_demo_for IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    simple_for_with_counter( out ).
    simple_for_with_date( out ).
    simple_for_with_mapping( out ).
    for_with_reduce( out ).
    for_with_where_condition( out ).
  ENDMETHOD.


  METHOD generate_data.
    rt_result = VALUE #(
      ( tkey = 'A' text = `Banana` number = 14 date = '20230101' )
      ( tkey = 'B' text = `Tomato` number = 12 date = '20230201' )
      ( tkey = 'C' text = `Apple` number = 23 date = '20231201' )
      ( tkey = 'E' text = `Strawberry` number = 31 date = '20230101' )
      ( tkey = 'F' text = `Salad` number = 20 date = '20230601' )
    ).
  ENDMETHOD.


  METHOD simple_for_with_counter.
    DATA(lt_numbers_until) = VALUE tt_numbers(
      FOR i = 1 UNTIL i >= 10 ( i )
    ).

    io_out->write( `Result table with NUMBERS (UNTIL):` ).
    io_out->write( lt_numbers_until ).

    DATA(lt_numbers_while) = VALUE tt_numbers(
      FOR i = 1 WHILE i <= 10 ( i )
    ).

    io_out->write( `Result table with NUMBERS (WHILE):` ).
    io_out->write( lt_numbers_while ).

    DATA(lt_numbers_then) = VALUE tt_numbers(
      FOR i = 1 THEN i + 3 WHILE i <= 10 ( i )
    ).

    io_out->write( `Result table with NUMBERS (THEN):` ).
    io_out->write( lt_numbers_then ).
  ENDMETHOD.


  METHOD simple_for_with_mapping.
    DATA(lt_base) = generate_data( ).

    DATA(lt_corresponding) = VALUE tt_easy_data(
      FOR ls_base IN lt_base ( CORRESPONDING #( ls_base ) )
    ).

    io_out->write( `Result table with CORRESPONDING:` ).
    io_out->write( lt_corresponding ).

    DATA(lt_simple_mapping) = VALUE tt_easy_data(
      FOR ls_base IN lt_base ( tkey = ls_base-tkey text = ls_base-text number = 1 )
    ).

    io_out->write( `Result table with simple mapping:` ).
    io_out->write( lt_simple_mapping ).
  ENDMETHOD.


  METHOD for_with_reduce.
    DATA(lt_base) = generate_data( ).

    DATA(ld_number) = REDUCE i(
      INIT num = 0
      FOR i = 1 THEN i + 3 WHILE i <= 10
      NEXT num = num + ( i * i )
    ).

    io_out->write( |Result from table reduce: { ld_number }| ).

    DATA(ld_sum) = REDUCE i(
      INIT sum = 0
      FOR ls_base IN lt_base
      NEXT sum = sum + ls_base-number
    ).

    io_out->write( |Sum of all items: { ld_sum }| ).

    DATA(ld_keys) = REDUCE string(
      INIT keys = ``
      FOR ls_base IN lt_base
      NEXT keys = keys && ls_base-tkey
    ).

    io_out->write( |All keys concatenated: { ld_keys }| ).
  ENDMETHOD.


  METHOD for_with_where_condition.
    DATA(lt_base) = generate_data( ).

    DATA(lt_where) = VALUE tt_easy_data(
      FOR ls_base IN lt_base INDEX INTO ld_idx WHERE ( date > '20230101' )
      ( tkey = ls_base-tkey text = ls_base-text number = ld_idx )
    ).

    io_out->write( `Result with date greater than 01/01/2023:` ).
    io_out->write( lt_where ).
  ENDMETHOD.


  METHOD simple_for_with_date.
    DATA(lt_date_variable) = VALUE tt_simple_data(
      FOR i = CONV d( '20230101' ) THEN i + 2 WHILE i <= '20230201' ( date = i )
    ).

    io_out->write( `Result table with date type:` ).
    io_out->write( lt_date_variable ).
  ENDMETHOD.
ENDCLASS.

 

Fazit

Schleifen mit FOR können bereits heute genutzt werden, machen aber die Lesbarkeit nicht unbedingt besser. Weiterhin sind sie kein Ersatz für LOOPs, da sie nur in vorgegebenen Statements arbeiten. Trotzdem sollte das neue Konstrukt beherrscht werden.

 

Quelle:
ABAP Dokumentation - FOR Iteration Expressions
ABAP Dokumentation - FOR Conditional Iteration


Enthaltene Themen:
Deep DiveFORSchleifeModernes ABAP
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP Deep Dive - Tabellenzugriff (intern)

Kategorie - ABAP

Schauen wir uns in diesem Artikel einmal den Tabellenzugriff auf interne Tabellen an und wie sie den READ TABLE ablösen.

03.02.2023

ABAP Deep Dive - VALUE

Kategorie - ABAP

In diesem Artikel wollen wir uns noch einmal das Value Statement in allen Ausprägungen anschauen und wie du es in deiner täglichen Arbeit nutzen kannst.

11.11.2022

ABAP Deep Dive - CORRESPONDING

Kategorie - ABAP

In diesem Artikel einmal etwas mehr über das neue Corresponding Statement und wie man es im Detail einsetzen kann. Dabei schauen wir einmal auf die zusätzlichen Features.

16.09.2022

ABAP Tools - Arbeiten mit Eclipse (Performance Analyse)

Kategorie - ABAP

In diesem Artikel befassen wir uns mit der Performance Analyse von Objekten über die ABAP Development Tools und die Möglichkeiten von Traces.

24.11.2023

ABAP Tipp - Adobe Formulare zu groß

Kategorie - ABAP

In diesem kleinen Tipp wollen wir uns anschauen, wieso im schlechtesten Fall Adobe Formulare größer werden, als sie eigentlich sein sollten.

18.11.2022