Idiomdrottning’s homepage

Named Let → Fold

OK, listen up, mediocre Scheme programmers! (Good ones probably already know this stuff.)

If you are comfortable with named lets but not with SRFI-1 folds, reduces, unfolds etc, this is the post for you.

Refactor your code so it matches one of the named lets below.

Copy the named let from this page into your code so you can change the token names as you wish. For example you might wanna change seed to 0 and proc to + and lis to a list of numbers. Or whatever you have. Maybe you replace proc with a lambda expression, that’s fine, IDC.

Then you know which fold, reduce, pair-fold or w/e you can slot in instead.

;; (fold proc seed l)
(let desc ((acc seed) (lis l))
  (if (null? lis)
      acc
      (desc (proc (car lis) acc) (cdr lis))))

;; (fold-right proc seed l)
(let desc ((acc seed) (lis l))
  (if (null? lis)
      acc
      (proc (car lis) (desc acc (cdr lis)))))

;; (pair-fold proc seed l)
(let desc ((acc seed) (lis l))
  (if (null? lis)
      acc
      (let ((tail (cdr lis)))
	(desc (proc lis acc) tail))))

;; (pair-fold-right proc seed l)
(let desc ((acc seed) (lis l))
  (if (null? lis)
      acc
      (proc lis (desc acc (cdr lis)))))

;; (reduce proc default l)
(if (null? l)
    default
    (let desc ((acc (car l)) (lis (cdr l)))
      (if (null? lis)
	  acc
	  (desc (proc (car lis) acc) (cdr lis)))))

;; (reduce-right proc default l)
(if (null? l)
    default
    (let desc ((lis l))
      (if (= 1 (length lis))
	  (car lis)
	  (proc (car lis) (desc (cdr lis))))))

;; (unfold stop? ->elem ->next start-value)
(let loop ((input start-value))
  (if (stop? input)
      '()
      (cons (->elem input)
	    (loop (->next input)))))

;; (unfold-right stop? ->elem ->next start-value)
(let loop ((input start-value) (lis '()))
  (if (stop? input)
      lis
      (loop (->next input)
	    (cons (->elem input)
		  lis))))

;; (unfold stop? ->elem ->next start-value ->tail)
(let loop ((input start-value))
  (if (stop? input)
      (->tail input)
      (cons (->elem input)
	    (loop (->next input)))))

;; (unfold-right stop? ->elem ->next start-value '(my own tail))
(let loop ((input start-value) (lis '(my own tail)))
  (if (stop? input)
      lis
      (loop (->next input)
	    (cons (->elem input)
		  lis))))

;; (fold proc seed l1 l2)
(let desc ((acc seed) (lis1 l1) (lis2 l2))
  (if
   ;; if any list runs out
   (zero? (apply min (map length (list lis1 lis2))))
      acc
      (desc (proc (car lis1) (car lis2) acc) (cdr lis1) (cdr lis2))))

But wait, Sandra, why should I put in a fold that I don’t understand in place of a named let that I do understand?

It’s great because it reuses canonical semantics rather than reimplementing them via an idiom or pattern.

Otherwise, people are gonna think it’s weird that you are reinventing the wheel instead of just using a fold or an unfold or w/e.

And by “people”, I mean me. I’m gonna think it’s weird.

See also: