2015-10-03 19 views

risposta

5

Nella maggior parte delle situazioni è possibile sostituire l'uso di iterate con 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 

Il vantaggio di for/fold è che si può iterare più variabili alla volta:

(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) 

Ciò restituirà due valori: 256 e 1/256.

Gli elementi di raccolta sono facili. Ecco l'esempio Fibonacci:

(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 

Il risultato è costituito da tre valori:

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

Inoltre - si aspettano 'per/fold' di essere più veloce di flusso. – soegaard

+0

Bene, ovviamente le comprensioni entusiaste sono molto più leggere dei flussi. ;-) In realtà dovrei scrivere un generatore di sequenza 'in-iterate' che può essere usato direttamente con' for' comprehensions. –

+0

@ ChrisJester-Young Questa è una bella idea. – soegaard

5

Non esiste un equivalente diretto all'interno delle procedure Racket incorporate, ma possiamo implementare qualcosa con una funzionalità simile utilizzando streams. Prova questo:

(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)))) 

Per esempio, ecco come gli esempi dalla Clojure documentation sarà simile a Racket:

(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)) fornisce stream-iterate direttamente.

È possibile utilizzare gli esempi di Óscar e sostituire , senza dover definire il proprio iterate. Inoltre, è possibile simulare il parametro destrutturazione del Clojure utilizzando 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

Sulla base dell'idea di Soegaard usare comprensioni ansiosi, ecco un generatore in-nest-sequence sequenza (disponibile anche on Code Review e 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 ...))])]))) 

Le sequenze generate da in-nest-sequence non terminano, quindi sarà necessario accoppiarle con eithe r una sequenza o una condizione di terminazione simile o #:break o simile. Per esempio (utilizzando l'esempio powers-of-two in risposta di Óscar):

;; 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 

Ed ecco l'esempio sequenza di Fibonacci:

;; 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

Non vedo l'ora di vedere anche la versione 'define-sequence-syntax' :-) – soegaard

+0

Sembra fantastico, anche se mi ci vorrà un po 'di tempo per digerirlo. Sono molto un racket n00b. – interstar

+0

@interstar Non esiste un modo veramente semplice per digerire le implementazioni di "in *"; sono scritti usando un linguaggio specifico specifico per il dominio (che si tratti di 'make-do-sequence' come ho usato, o' define-sequence-syntax' come suggerito da soegaard). Ma se ti piace imparare qualcosa di nuovo, sicuramente servirà a questo scopo! –