循環リストを使う


Tags: R6RS, SRFI, リスト

リストの最後の cdr が自分自身を指すように変更することで循環リストを作ることができる。

(import (rnrs)
        (rnrs mutable-pairs))

(let ((x (list 0 1)))
  (set-cdr! (cdr x) x)
  x)
;; => (0 1 0 1 0 1 0 1 ...)

SRFI 1 にはこれを一般化した circular-list 手続きがある。

(import (rnrs) (only (srfi :1) circular-list))

(circular-list 0) ; => (0 0 0 ...)
(circular-list 1 3 5) ; => (1 3 5 1 3 5 1 3 5 ...)

これに加えて、 SRFI 1 の map や for-each は不揃いなリスト群を引数に取ることのできることを利用すると、リストの要素に規則的に処理を行なうことができる。例えば、リストの要素を出力し、奇数番目の要素の後には空白を、偶数番目の要素のあとには改行を出力するような処理が次のように書ける。

(import (except (rnrs) for-each)
        (only (srfi :1) circular-list for-each))

(for-each (lambda (f g x) (f x) (g))
          (circular-list display)
          (circular-list (lambda () (display " ")) newline)
          '(1 2 3 4 5 6))
;; -> 1 2
;;    3 4
;;    5 6
;;

リストの要素をカンマ区切りで出力する処理を次のように書ける。

(import (except (rnrs) for-each)
        (only (srfi :1) circular-list for-each))

(for-each (lambda (f x) (f x))
          (cons display (circular-list (lambda (x) (display ",") (display x))))
          '(1 2 3 4))
;; -> 1,2,3,4