Например,
(require racket/generator)
(define f add1)
(define init 0)
(in-producer (generator () (let loop ([x init]) (yield x) (loop (f x)))))
Есть ли лучший способ сделать это? Я не очень люблю генераторы, так как у них есть скрытые состояния.
Использование потоки, вероятно, является самым простым:
(require racket/stream)
;; X [X -> X] -> [Streamof X]
(define (repeated-fn-stream init f)
(stream-cons init (repeated-fn-stream (f init) f)))
(repeated-fn-stream 0 add1)
В качестве альтернативы, используя последовательности и make-do-sequence
:
(require racket/sequence)
;; X [X -> X] -> [Sequenceof X]
(define (repeated-fn-sequence init f)
;; A "Pos" is an X that's the result of applying f repeatedly to init
(define (pos->element pos) pos)
(define (next-pos pos) (f pos))
(define init-pos init)
(make-do-sequence
(λ ()
(values pos->element
next-pos
init-pos
#false
#false
#false))))
(repeated-fn-sequence 0 add1)
Если вы хотели использовать последовательности, и, вы хотели использовать define-sequence-syntax
, чтобы циклы for
специализировались на нем:
(для "чистого" функционала это совершенно не нужно, но может иметь другие характеристики производительности)
(require (for-syntax syntax/parse))
(define-sequence-syntax in-repeated-fn-sequence
(λ () #'repeated-fn-sequence) ; when used as a normal expression
(syntax-parser ; when used *directly* as a for-loop clause
[[(x) (_ init-expr f-expr)]
#'[(x) (:do-in
([(init) init-expr] [(f) f-expr])
#true
([x init])
#true
()
#true
#true
[(f x)])]]))
(for/list ([x (in-repeated-fn-sequence 0 add1)]
[i (in-range 10)])
x)
При использовании define-sequence-syntax
следует убедиться, что для всего существует «единая точка истины».. Из-за этого вы часто видите такую закономерность:
(define-sequence-syntax in-___
(λ () #'in-___/proc) ; when used as a normal expression
(syntax-parser
....everything that defines the actual functionality....))
;; This is completely determined by the sequence-syntax above,
;; that way there is NO duplicated functionality and NO chance for
;; it to get "out of sync".
(define (in-___/proc parameter ...)
(for/stream ([elem (in-___ parameter ...)])
elem))
Это означает, что как только вы решите использовать define-sequence-syntax
, вы должны определить repeated-fn-sequence
функция в терминах этого:
(define (repeated-fn-sequence init f)
(for/stream ([elem (in-repeated-fn-sequence init f)])
elem))
Таким образом, если in-repeated-fn-sequence
необходимо заменить для исправления ошибки или переключения представлений, версия функции автоматически изменится вместе с ней.
Да, они кешируют или "запоминают" результаты
Лучшая функция для этой работы - развернуться… но, к сожалению, Racket не поддерживает встроенную операцию sequence-unfold
или stream-unfold
. Однако в библиотеке stream-unfold
есть является операция srfi/41
, которая соответствует вашим потребностям. Вы можете увидеть это в действии с помощью следующей программы:
#lang racket
(require (only-in srfi/41 stream-unfold))
(define nats (stream-unfold identity (const #t) add1 0))
(for/list ([i (in-range 20)] [n nats]) n)
Это дает следующий результат:
'(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)
Если вы не хотите использовать srfi/41
, вы можете написать stream-unfold
самостоятельно в терминах API racket/stream
без особых трудностей и без сохранения состояния:
(define (stream-unfold mapper pred? gen base)
(let loop ([base base])
(if (pred? base)
(stream-cons (mapper base) (loop (gen base)))
empty-stream)))
Спасибо. Я не знал, что это называется разворачиванием.
Спасибо. Я не понимал, что stream-cons - это особая форма. Похоже, для этого используется много памяти. Кешируются ли результаты потоками? Обновлено: Я изучаю последовательность действий.