[Racket] SplitBy function I always wanted + fortune program in ~5 SLOC

A function in Wolfram Mathematica:

SplitBy[list,f]
        splits list into sublists consisting of runs of successive elements that give the same value when f is applied.

( src )

The function is incredibly useful when parsing, for example, a fortune file, which is present in any *NIX system, like:
the more general problem may be easier to solve
    -- G.Polya
%
The world doesn't live off jam and fancy perfumes - it lives off bread and meat and potatoes. Nothing changes. All the big fancy stuff is sloppy stuff that crashes. I don't need dancing baloney - I need stuff that works. That's not as pretty, and just as hard.
    -- Theo de Raadt
%
As a friend of mine, the computer scientist Guy Steele, once put it, “LISP is a high-level language, but you can still feel the bits sliding between your toes.”
   -- Daniel Hillis -- The Pattern On The Stone: The Simple Ideas That Make Computers Work
%

How do you pick a random element if it may consist of several lines? Here I split the fortune file using my own reimplementation of this function in Racket:

#lang racket

; another function I didn't find:
(provide pick-random)
(define (pick-random lst)
  (list-ref lst (random (length lst))))

; works almost like http://reference.wolfram.com/language/ref/SplitBy.html in Mathematica:
(define (drop-first-elem-if l pred)
  (if (pred (first l))
      (drop l 1)
      l))

(define (drop-empty-lists l)
  (filter (λ (x) (not (null? x))) l))

(provide split-list-into-sublists)
(define (split-list-into-sublists xs pred)
  (if (null? xs)
      '()
      (let* ((new-list (drop-first-elem-if xs pred))
             (first-chunk (takef new-list (negate pred)))
             (rest-of-list (dropf new-list (negate pred))))
        (cons first-chunk (drop-empty-lists (split-list-into-sublists rest-of-list pred))))))

; main program

(define (pick-random-fortune-from filename)
  (define sliced
    (split-list-into-sublists
     (file->lines filename)
     (λ (n) (equal? "%" n))))
  (map (λ (s) (displayln s)) (pick-random sliced))) ; print each line

(void (pick-random-fortune-from "/home/i/tmp/fortunes.txt")) ; "void" to suppress printing "void"

Isn't it nice? Of course, my implementation is crude and slow. If you can suggest better or more efficient, please drop me email.


List of my other blog posts.

Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.