Switched the guile xjoin to tail-recursive. The Bs are in columns since they divide the line length. The Fs are on receding diagonals since their multiple is one below the line length.
scheme@(guile-user)> (display ((lambda (rec xjoin empty pad n spec) ((lambda (xjoin) ((lambda (pad fb) ((lambda (cell) (xjoin n (lambda (k) (string-append (xjoin n (lambda (j) (cell (+ (* n k) j 1))) " ") "\n")) "")) (lambda (n) (pad (empty (fb n) n))))) (lambda (s) (if (>= (string-length s) pad) s (string-append (xjoin (- pad (string-length s)) (lambda (k) " ") "") s))) (lambda (n) (xjoin (vector-length spec) (lambda (k) (if (= (remainder n (car (vector-ref spec k))) 0) (cadr (vector-ref spec k)) "")) "")))) (lambda (n fun sep) (rec xjoin n fun sep "")))) (lambda (f . args) (apply f (cons f args))) (lambda (self n fun sep acc) (if (<= n 0) "" (if (= n 1) (string-append (fun 0) acc) (self self (- n 1) fun sep (string-append sep (fun (- n 1)) acc))))) (lambda (s n) (if (= (string-length s) 0) (number->string n) s)) 2 10 #((3 "F") (5 "B"))))
1 2 F 4 B F 7 8 F B
11 F 13 14 FB 16 17 F 19 B
F 22 23 F B 26 F 28 29 FB
31 32 F 34 B F 37 38 F B
41 F 43 44 FB 46 47 F 49 B
F 52 53 F B 56 F 58 59 FB
61 62 F 64 B F 67 68 F B
71 F 73 74 FB 76 77 F 79 B
F 82 83 F B 86 F 88 89 FB
91 92 F 94 B F 97 98 F B
scheme@(guile-user)>