REBOL for COBOL programmers
Why so dense?
Date written: May 18, 2016
Why do the REBOL samples on the internet look so dense?
Here are two versions of a function that takes a string and a length and returns a string of the given length consisting of the original string plus enough spaces added on the right to make a result of the desired length. The first version is one I wrote by plodding along in my COBOL-like way, and the second is a rewritten version made after a bit of advice from a more knowledgeable REBOL person.
SPACEFILL: func [ "Left justify a string, pad with spaces to specified length" INPUT-STRING FINAL-LENGTH /local TRIMMED-STRING LENGTH-OF-TRIMMED-STRING NUMBER-OF-SPACES-TO-ADD FINAL-PADDED-STRING ] [ TRIMMED-STRING: copy "" TRIMMED-STRING: trim INPUT-STRING LENGTH-OF-TRIMMED-STRING: length? TRIMMED-STRING either (LENGTH-OF-TRIMMED-STRING < FINAL-LENGTH) [ NUMBER-OF-SPACES-TO-ADD: (FINAL-LENGTH - LENGTH-OF-TRIMMED-STRING) FINAL-PADDED-STRING: copy TRIMMED-STRING loop NUMBER-OF-SPACES-TO-ADD [ append FINAL-PADDED-STRING " " ] ] [ FINAL-PADDED-STRING: COPY "" FINAL-PADDED-STRING: copy/part TRIMMED-STRING FINAL-LENGTH ] ]
SPACEFILL: func [ "Left justify a string, pad with spaces to specified length" INPUT-STRING FINAL-LENGTH ] [ head insert/dup tail copy/part trim INPUT-STRING FINAL-LENGTH #" " max 0 FINAL-LENGTH - length? INPUT-STRING ]
The first version plods along as you might expect. It trims the input, calculates how many spaces to add (if any), adds them to the end, and returns the result. It uses temporary variables along the way to make things easier to follow.
The second version takes advantage of the flow of data allowed by the REBOL method of calling functions and passing the results to other function. The innermost function trims the input. The next step copies off part of the input string, either as many as there are, or as many as we ultimately want if we want fewer than we have. Next we go to the end of the copied part of the input string and add spaces to the end of it. We add either zero if we don't need any, or we add as many as we need to hit the target length (calculated as the final length we want minus the number we currently have). After that we re-position to the head if the inut string and return the input string. This version needs no temporary variables because the original input string flows through a bunch of functions that work on it.
So now set the example aside, and think about what happens in a compiled language where you want to set up an integer counter and add to it. In a compiled language, everything about that operation is identified at compile time and set up at compile time. The identifier for the counter is parsed, a couple bytes are set up at a known memory address, an instruction is generated to add to the counter, and the instruction could theoretically be one machine instruction depending on the hardware.
In an interpreted language, at run time, the identifier is parsed, a data structure is generated to hold that integer plus other attrubutes to identify it as an integer, and the instruction to add to the counter has to look up the item in some sort of table, discover that it is an integer and can be added to, and then do the calculation.
In other words, any addition of a variable to an interpreted program adds a lot to the program. So when you are writing in an interpreted language, it makes sense to avoid adding things you don't have to. REBOL accomplishes that by the density you observe in the published code samples.