I know I said I was going to focus on Clojure, but I looked at some other things in the past week.
I am looking into some Amazon certification for AWS. There is a big push to get certified at my current employer, and I would like to do something different.
I started the org-mode tutorial from Udemy.
I also looked at The Seasoned Schemer. I think that having already gone through Simply Scheme I may have gotten some of the concepts. A lot of it deals with recursion.
Chapter 12 dealt with “letrec”. letrec is like let, but instead of binding a name to a variable or data, you can bind a name to a function, and you can call that function recursively.
One thing this can allow you to do is it can relieve you of the need to call a helper function. In Simply Scheme, I made all the functions tail-recursive. To do this, you usually need a parameter that is the “accumulator” or the output (see this answer on Stack Overflow or my post here).
In The Seasoned Schemer, they use letrec for functions in which one argument does not change and one does, such as checking if an atom is a member of a list, or performing a union of two lists:
(define (union-letrec s1 s2) (letrec ([U (lambda (set-1) (cond [(null? set-1) s2] [(ss-sc:member? (car set-1) s2) (U (cdr set-1))] [else (cons (car set-1) (U (cdr set-1)))]))]) (U s1)))
I know I think the whole argument about Lisp/Scheme being hard to read because of all the parentheses is usually exaggerated, here I think for these letrec examples there might be something to it. These were hard to type correctly from the book. For example, the internal function has to be after the “(set-1)” which is the argument to the “lambda”, but before the closing parens for “lambda”. And the call to the internal function has to be before the closing call to “letrec”. You can have multiple functions defined in a “letrec”, and your call to the internal functions can be in a cond.
A helper function to do the actual work with tail-recursion might be more lines of code, but I think it is easier to understand.
Chapter 13 is about let/cc, combining let with “call-with-current-continuation”. I think some people consider this Scheme’s defining feature.
They have a function rember-upto-last which takes an atom and a list of atoms and removes everything in the list before the last instance of the atom as well as the atom.
Here it is:
(define (rember-upto-last a lat) (let/cc skip (letrec ([R (lambda (lat) (cond [(null? lat) '()] [(ss-sc:equal5? (car lat) a) (skip (R (cdr lat))) ; (R (cdr lat)) ] [else (cons (car lat) (R (cdr lat)))]))]) (R lat))))
Here is a tail-recursive version:
(define (rember-upto-last-r a lat) (let/cc skip (letrec ([R (lambda (lat outp) (ss-sc:display-all "Calling R w/lat: " lat " and outp: " outp) (cond [(null? lat) (reverse outp)] [(ss-sc:equal5? (car lat) a) (skip (R (cdr lat) '())) ;(R (cdr lat) '()) ] [else (R (cdr lat) (cons (car lat) outp))]))]) (R lat '()))))
Both of these have a commented out call to “(R (cdr lat)” (with a second arg in the tail-recursive version) below the call to “skip”.[Note 1]. In the tail-recursive version, either call gives the proper result. In the regular version, we are building our answer through a chain of calls to “cons”. The “skip” eliminates all the pending “cons” calls, and the function is called anew with whatever the then-current values are. In the tail-recursive version, we can get rid of those “pending” values ourselves.
I think Java might get continuations soon.
[Note 1]: For some reason, the code formatter I am using does not always color comments correctly. In all variants of Lisp that I have seen, comments start with a semi-colon.
Image from Psalterium Gallicanum with Cantica, a 9th century manuscript from the monastery of St. Gall, housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.