2015-10-03 2 views
9

Сегодня я играю с Racket и пытаюсь создать неопределенную последовательность чисел на основе нескольких приложений одной и той же функции.Что эквивалентно функции повторения Clojure в Racket

В Clojure я использовал бы функцию итерации, но я не уверен, что было бы эквивалентно в Racket.

ответ

5

В большинстве случаев вы можете заменить использование iterate с for/fold.

> (define (mult2 x) (* x 2)) 

> (for/fold ([x 1]) ; the initial value of x is 1 
      ([i 8]) ; count i=0,...,7 
    (mult2 x))   ; put x = (mult2 x) 
256 

Преимущество for/fold является то, что вы можете перебирать несколько переменных одновременно:

(define (mult2 x) (* x 2)) 
(define (div2 x) (/ x 2)) 

(for/fold ([x 1]  ; bind x to 1 
      [y 1])  ; bind y to 1 
      ([i 8])  ; i=0,...,7 
    (values (mult2 x)  ; put x = (mult2 x) 
      (div2 y))) ; put y = (div2 y) 

Это будет возвращать два значения: 256 и 1/256.

Элементы сбора легко. Вот пример Фибоначчи:

(for/fold ([fs '(1)]  ; list of fibonacci numbers generated so far 
      [f1 1]  ; a fibonacci number 
      [f2 1])  ; the following fibonacci number 
      ([i 10])  ; i = 0,...,9 
    (values (cons f2 fs) ; cons the new fibonacci number to the list fs 
      f2    ; put f1 = (the old) f2 
      (+ f1 f2))) ; put f2 = (the old) f1+f2 

Результат состоит из трех значений:

'(89 55 34 21 13 8 5 3 2 1 1) 
89 
144 
+0

Также - жди 'для/fold' более быстрее, чем поток. – soegaard

+0

Ну, конечно, страстные мысли гораздо легче, чем потоки. ;-) На самом деле я должен написать генератор последовательности 'in-iterate', который можно использовать напрямую с помощью' for'. –

+0

@ ChrisJester-Young Это хорошая идея. – soegaard

5

Внутри встроенных процедур Racket нет прямого эквивалента, но мы можем реализовать что-то с аналогичной функциональностью, используя streams. Попробуйте это:

(define (stream-take m s) 
    (if (zero? m) 
     '() 
     (cons (stream-first s) 
      (stream-take (sub1 m) (stream-rest s))))) 

(define (iterate f x) 
    (stream-cons x (iterate f (f x)))) 

Например, вот как примеры из Clojure documentation будет выглядеть в Ракетка:

(stream-take 5 (iterate add1 5)) 
=> '(5 6 7 8 9) 

(stream-take 10 (iterate (curry + 2) 0)) 
=> '(0 2 4 6 8 10 12 14 16 18) 

(define powers-of-two (iterate (curry * 2) 1)) 
(stream-ref powers-of-two 10) 
=> 1024 
(stream-take 10 powers-of-two) 
=> '(1 2 4 8 16 32 64 128 256 512) 

(define fib 
    (stream-map first 
       (iterate (lambda (pair) 
         (list (second pair) 
           (+ (first pair) (second pair)))) 
         '(0 1)))) 
(stream-take 10 fib) 
=> '(0 1 1 2 3 5 8 13 21 34) 
6

SRFI 41 ((require srfi/41)) обеспечивает stream-iterate непосредственно.

Вы можете использовать примеры Óscar и заменить stream-iterate везде, где вы видите iterate, без необходимости определять свои собственные iterate. Кроме того, вы можете моделировать параметр деструктурирующие Clojure, используя match-lambda:

(require srfi/41) 
(define fib 
    (stream-map first 
       (stream-iterate (match-lambda 
           ((list a b) 
           (list b (+ a b)))) 
           '(0 1)))) 
(stream->list 10 fib) ; => (0 1 1 2 3 5 8 13 21 34) 
4

Основываясь на идее soegaard, чтобы использовать нетерпеливые постижений, вот генератор in-nest-sequence последовательность (также on Code Review и as a Gist):

#lang racket 
(require (for-syntax unstable/syntax)) 
(provide (rename-out [*in-nest-sequence in-nest-sequence])) 

(define in-nest-sequence 
    (case-lambda 
    [(func init) 
    (make-do-sequence 
     (thunk (values identity func init #f #f #f)))] 
    [(func . inits) 
    (make-do-sequence 
     (thunk (values (curry apply values) 
        (lambda (args) 
         (call-with-values (thunk (apply func args)) list)) 
        inits #f #f #f)))])) 

(define-sequence-syntax *in-nest-sequence 
    (lambda() #'in-nest-sequence) 
    (lambda (stx) 
    (syntax-case stx() 
     [[(x ...) (_ func init ...)] 
     (unless (= (syntax-length #'(x ...)) (syntax-length #'(init ...))) 
     (raise-syntax-error 'in-nest-sequence 
          (format "~a values required" (syntax-length #'(x ...))) 
          stx #'(init ...))) 
     (with-syntax ([for-arity (syntax-length #'(init ...))] 
        [(value ...) (generate-temporaries #'(init ...))] 
        [(y ...) (generate-temporaries #'(init ...))]) 
     #'[(x ...) (:do-in ([(f) func]) 
          (unless (procedure-arity-includes? f for-arity) 
           (raise-arity-error f (procedure-arity f) init ...)) 
          ([value init] ...) 
          #t 
          ([(x ...) (values value ...)] 
          [(y ...) (f value ...)]) 
          #t 
          #t 
          (y ...))])]))) 

Последовательности, генерируемые in-nest-sequence, не заканчиваются, поэтому вы захотите соединить его с eithe r последовательность, которая делает, или #:break или аналогичное условие завершения. Например (используя powers-of-two пример в ответ Оскара):

;; first ten powers of 2 
(for/list ((_ (in-range 10)) 
      (x (in-nest-sequence (curry * 2) 1))) 
    x) 
; => (1 2 4 8 16 32 64 128 256 512) 

;; powers of 2 above 10,000 and below 1,000,000 
(for/list ((x (in-nest-sequence (curry * 2) 1)) 
      #:when (> x 10000) 
      #:break (> x 1000000)) 
    x) 
; => (16384 32768 65536 131072 262144 524288) 

;; first power of 2 above 10,000,000 
(for/first ((x (in-nest-sequence (curry * 2) 1)) 
      #:when (> x 10000000)) 
    x) 
; => 16777216 

А вот пример последовательности Фибоначчи:

;; first ten Fibonacci numbers 
(for/list ((_ (in-range 10)) 
      ((x y) (in-nest-sequence (lambda (a b) (values b (+ a b))) 0 1))) 
    x) 
; => (0 1 1 2 3 5 8 13 21 34) 

;; first Fibonacci number above 10,000,000 
(for/first (((x y) (in-nest-sequence (lambda (a b) (values b (+ a b))) 0 1)) 
      #:when (> x 10000000)) 
    x) 
; => 14930352 
+1

Не могу дождаться, чтобы увидеть версию 'define-sequence-syntax' тоже :-) – soegaard

+0

Это выглядит великолепно, хотя я собираюсь потратить немного времени, чтобы переварить его. Я очень ракета n00b. – interstar

+0

@interstar Нет никакого простого способа переварить 'in *' реализации; они написаны с использованием специального языка, специфичного для домена (будь то 'make-do-sequence', как я использовал, или' define-sequence-syntax', как предлагалось). Но если вы хотите узнать что-то новое, это определенно послужит этой цели! –

Смежные вопросы