SET/NEXT/PREVIOUS for queues

Reading the post How to loop over a queue and delete SOME entries reminded me that Clarion lacks an instruction to loop over all the values of a queue without using an index. Most languages do, even Clarion# (FOREACH) and the Template language (#FOR).

Using indexes forces you to declare index variables or, if you are lazy, to use implicit variables. I used to declare a few variables (I LONG, J LONG, etc.) in all procedures where I used queues, but this got me when by mistake I reused a variable in another routine. Using implicit variables is worst.

Now, for safety, I declare an index variable for each queue:

myQ QUEUE
...
END
    
ImyQ LONG
...

LOOP ImyQ = 1 TO RECORDS(myQ)
  GET(myQ,ImyQ)
  !Some code
.

But I decided to look for a “better” way, and it turns out it is possible to loop over a queue without an index in Clarion:

GET(myQ,0)
LOOP
  GET(myQ,POINTER(myQ)+1)
  IF ERRORCODE() THEN BREAK.
.

And adding a few trivial global procedures like these:

SET                 PROCEDURE(QUEUE q)
  CODE
  GET(q,0)

NEXT                PROCEDURE(QUEUE q)!,LONG
  CODE
  GET(q,POINTER(q)+1)
  RETURN ERRORCODE()

PREVIOUS            PROCEDURE(QUEUE q)!,LONG
ptr                   LONG
  CODE
  ptr = POINTER(q)
  IF ptr
    GET(q,ptr-1)
  ELSE
    GET(q,RECORDS(q))
  .
  RETURN ERRORCODE()

It is possible to write:

SET(myQ)
LOOP 
  NEXT(myQ)
  IF ERRORCODE() THEN BREAK.
  !Some code
.

Or even:

SET(myQ)
LOOP UNTIL NEXT(myQ)
  !Some code
.

SET(myQ)
LOOP UNTIL PREVIOUS(myQ)
  DELETE(myQ)
.

Of course, there is a performance impact. I wrote a small benchmark and got these results:

LOOP I: 2.33 
LOOP GET/ERRORCODE: 2.61 12.02% 
LOOP SET/NEXT: 2.65 13.73% 

LOOP DELETE I: 2.93 
LOOP DELETE SET/PREVIOUS: 3.6 22.87% 

Meaning that there is an increase of about 13% when using SET/NEXT and about 22% when using SET/PREVIOUS. But depending on what you are doing inside the loop (printing, saving to disk, etc.), the difference may be negligible.

Here’s a project with all the code: SET/NEXT/PREVIOUS for queues (github.com)

HTH somebody.

7 Likes

That’s cleverly lean. Nicely done!

1 Like

Fwiw, I usually use the In Memory driver to work with queue processing. Then I can use the usual file processing, e.g. LOOP and DELETE. It is less stress on my brain.
When I want to show a DIRECTORY listing in a browse LIST, I copy the queue to an In Memory file that has keys for the file name, date/time and file size.

2 Likes