В приложении, в котором я работаю в 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, если нет более простого более простого способа сделать это, каким-то образом избавиться от "call-in-values" и необходимости переверните список в процедуре и т.д., возможно, каким-то образом, совершенно иным.
Моя первая попытка была очень плохо основана на шаблоне с коллекционером в "The Little Schemer" и это было еще менее прямолинейно, чем выше:
(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)])))
То, что я ищу, - это что-то простое, краткое и понятное.