В приложении, над которым я работаю в Racket, мне нужно взять список чисел и разбить его на под-списки последовательных номеров: (В реальном приложении, на самом деле я буду разбивать пары, состоящие из числа и некоторых данных, но принцип тот же.)
т.е. если моя процедура называется chunkify
, то:
(chunkify '(1 2 3 5 6 7 9 10 11)) -> '((1 2 3) (5 6 7) (9 10 11))
(chunkify '(1 2 3)) -> '((1 2 3))
(chunkify '(1 3 4 5 7 9 10 11 13)) -> '((1) (3 4 5) (7) (9 10 11) (13))
(chunkify '(1)) -> '((1))
(chunkify '()) -> '(())
и т.д.
Я придумал следующее в Racket:
#lang racket
(define (chunkify lst)
(call-with-values
(lambda ()
(for/fold ([chunk '()] [tail '()]) ([cell (reverse lst)])
(cond
[(empty? chunk) (values (cons cell chunk) tail)]
[(equal? (add1 cell) (first chunk)) (values (cons cell chunk) tail)]
[else (values (list cell) (cons chunk tail))])))
cons))
Это прекрасно работает, но мне интересно, учитывая выразительность Racket, если нет более простого и простого способа сделать это, какого-то способа избавиться от «вызова -со значениями -» и необходимости перевернуть список в процедуре и т. д., возможно, каким-то образом совершенно другим.
Моя первая попытка была очень приблизительно основана на образце с коллектором в «Маленьком интригане» , и это было еще менее прямолинейно, чем выше:
(define (chunkify-list lst)
(define (lambda-to-chunkify-list chunk) (list chunk))
(let chunkify1 ([list-of-chunks '()]
[lst lst]
[collector lambda-to-chunkify-list])
(cond
[(empty? (rest lst)) (append list-of-chunks (collector (list (first lst))))]
[(equal? (add1 (first lst)) (second lst))
(chunkify1 list-of-chunks (rest lst)
(lambda (chunk) (collector (cons (first lst) chunk))))]
[else
(chunkify1 (append list-of-chunks
(collector (list (first lst)))) (rest lst) list)])))
То, что я ищу, это что-то простое, лаконично и прямолинейно.