I have been reacquainting myself with Pick after a long hiatus, using
String Database as my current Pick environment. I used Pick
professionally over 20 years ago. Over the last year, I've been writing
some programs for a hobby project (and to see how much of the language I
still remember). The project has been a combination of code I wrote
myself and code generated by an AI assistant. Because my AI assistant
didn't fully understand multivalue file structure, it wound up writing
data with trailing empty attributes and multivalues.
I treated the data cleanup program as a coding challenge. I wrote the
first version to be efficient, readable, maintainable, and to use
features that weren't available in the Pick environments I used back in
the day (GA Zebra, Fujitsu, R83, D3). The specific features I
incorporated are CONTINUE, LOCAL FUNCTION, and VARIABLE[n].
After deliberately over-engineering the routine, I wondered how
quickly I could write a "fast and dirty" version. The latter took about
ten minutes to write and another 20 or so minutes to test (it might have
been quicker if there hadn't been a few typos in the early draft).
Since I'm working on my own, there's no opportunity for peer review
at home. Therefore, I'm sharing the code with the pros in this group and
looking for feedback. Specifically:
-
Which of the two versions do you prefer, and why?
-
Please review my coding approaches. I think I've reached my old level of
proficiency, but there's always room for improvement, and I appreciate
suggestions.
If you have a use for either utility, feel free to use it. A
progress indicator would be an easy addition if you find one useful.
PROGRAM TRIM.TRAILING.DELIMITERS
* COPYRIGHT 2026 ROBERT HERBIN
* SIMPLE PROGRAM TO SHORTEN ALL RECORDS IN A FILE BY REMOVING TRAILING AM, VM, AND SVM
* 8/14/2026 Added check of last character of string to prevent unnecessary reassignment of identical values
* 8/14/2026 Added check for trailing SVMs within each attribute
* 8/14/2026 - track a change flag rather than compare original record to modified record
* 8/20/2026 - Assigned dedicated delimiter search variables for each level rather than reassigning during program execution
* note that the sequence of these characters is important; the VM-level INDEX search for trailing SVMs relies on it
$DEBUG
$CATALOGUE
DEFFUN TRUNCATE (TEXT, STRIP.CHARS) LOCAL
PROMPT ""
RECORD.STRIPCHARS = @SVM:@VM:@AM ; ATTRIBUTE.STRIPCHARS = @SVM:@VM ; MV.STRIPCHARS = @SVM
CRT "ENTER FILE TO PROCESS: ":
INPUT FILENAME
OPEN FILENAME TO FILE.VAR ELSE STOP "CANNOT OPEN ": FILENAME
SELECT FILE.VAR
LOOP
READNEXT ID ELSE STOP "PROCESSING COMPLETE"
READU REC FROM FILE.VAR, ID ELSE CONTINUE
CHANGE.FLAG = 0
* STRIP TRAILING DELIMITERS FROM END OF RECORD
IF INDEX(RECORD.STRIPCHARS, REC[1], 1) THEN
REC = TRUNCATE(REC, RECORD.STRIPCHARS)
CHANGE.FLAG = 1
END
* LOOP THROUGH ATTRIBUTES. STRIP TRAILING VMs/SVMs FROM THE ATTRIBUTE
* THEN SEARCH FOR TRAILING SVMs AT VM BOUNDARIES WITHIN THE ATTRIBUTE
LAST.ATT = DCOUNT(REC, @AM)
FOR ATT.CTR = 1 TO LAST.ATT
ATT.CHANGE.FLAG = 0
CUR.ATT = REC<ATT.CTR>
IF INDEX(ATTRIBUTE.STRIPCHARS, CUR.ATT[1], 1) GT 0 THEN
CUR.ATT = TRUNCATE(CUR.ATT, ATTRIBUTE.STRIPCHARS)
ATT.CHANGE.FLAG = 1
END
IF INDEX(CUR.ATT, ATTRIBUTE.STRIPCHARS, 1) GT 0 THEN ;* There is at least one SVM at a VM boundary (trailing SVM)
LAST.VM = DCOUNT(CUR.ATT, @VM)
FOR VM.CTR = 1 TO LAST.VM-1 ;* LAST.VM already checked at attribute level, stop at LAST.VM - 1
IF INDEX(MV.STRIPCHARS, CUR.ATT<1, VM.CTR>[1], 1) GT 0 THEN
CUR.ATT<1, VM.CTR> = TRUNCATE(CUR.ATT<1, VM.CTR>, MV.STRIPCHARS)
END
NEXT VM.CTR
ATT.CHANGE.FLAG = 1
END
IF ATT.CHANGE.FLAG = 1 THEN
REC<ATT.CTR> = CUR.ATT
CHANGE.FLAG = 1
END
NEXT ATT.CTR
IF CHANGE.FLAG = 1 THEN WRITE REC ON FILE.VAR, ID ELSE RELEASE FILE.VAR, ID
REPEAT
STOP
LOCAL FUNCTION TRUNCATE(TEXT, STRIP.CHARS)
LENTEXT = LEN(TEXT)
FOR CHAR.CTR = LENTEXT TO 1 STEP -1
IF INDEX(STRIP.CHARS, TEXT[CHAR.CTR, 1], 1) > 0 THEN ;* strip char; skip unless first char
IF CHAR.CTR = 1 THEN TEXT = "" ;* reached start; TEXT is all strip chars
END ELSE ;* non strip char
IF CHAR.CTR < LENTEXT THEN TEXT = TEXT[1, CHAR.CTR]
EXIT
END
NEXT CHAR.CTR
RETURN TEXT
END
*********************************************************************************************
PROGRAM TRIM.TRAILING.DELIMITERS.TERSE
* COPYRIGHT 2026 ROBERT HERBIN
* ALTERNATE VERSION OF TRIM.TRAILING.DELIMITERS
* THE INTENT OF THIS VERSION WAS TO WRITE THE PROGRAM AS QUICKLY AS POSSIBLE
* AND FOR THE RESULTING CODE TO BE AS TERSE AS POSSIBLE.
* INEFFICIENCY IN THE CODE IS TOLERABLE BECAUSE THIS IS A "ONE OFF" PROGRAM,
* IF IT TAKES EXTRA TIME TO RUN, NO BIGGIE (e.g. DCOUNT IN FOR STATEMENT)
*
$DEBUG
$CATALOG
PROMPT ""
CRT "ENTER FILE TO PROCESS: ":
INPUT FILENAME
OPEN FILENAME TO FILE.VAR ELSE STOP "CANNOT OPEN ":FILENAME
SELECT FILE.VAR
LOOP
READNEXT ID ELSE EXIT
READU REC FROM FILE.VAR, ID THEN
CHANGE.FLAG = 0
FOR A = DCOUNT(REC, @AM) TO 1 STEP -1 UNTIL CONVERT(@VM:@SVM, "", REC<A>) <> ""
DEL REC<A> ; CHANGE.FLAG = 1
NEXT A
FOR A = 1 TO DCOUNT(REC, @AM)
FOR V = DCOUNT(REC<A>, @VM) TO 1 STEP -1 UNTIL CONVERT(@SVM, "", REC<A, V>) <> ""
DEL REC<A, V> ; CHANGE.FLAG = 1
NEXT V
FOR V = 1 TO DCOUNT(REC<A>, @VM)
FOR S = DCOUNT(REC<A, V>, @SVM) TO 1 STEP -1 UNTIL REC<A, V, S> <> ""
DEL REC<A, V, S> ; CHANGE.FLAG = 1
NEXT S
NEXT V
NEXT A
IF CHANGE.FLAG = 1 THEN WRITE REC ON FILE.VAR, ID ELSE RELEASE FILE.VAR, ID
END
REPEAT
STOP
END