How to implement a Python-style generator in a circuit (Racket or ChezScheme)?

today I am solving the N-queen problem with Scheme, but it is very slow compared to the same version of Python. at N = 8, the circuit takes 90 seconds! I know that one reason is that I cannot use the generator in Scheme, my code must first create large lists, which is a nightmare for memory and calculation.

There are several topics about the generator in Scheme, this one of them is the only one that I found, maybe useful, but, unfortunately, it does not work in both racket or chez.

In fact, I just want a simple version of the python generator, that is, I do not form the entire list, just send me one value at a time. i.e:

(range 100000) ; will consume a large memory

(define g (generator 100000)) ; will do nothing
(next g) ;0 <-you call it with next one time, it returns one value
(next g) ;1
;...
(next g) ;100000
(next g) ;return a value that indicates the end, such as #f.

, . . !

N-queen, :

(define (range n)
    (define (recur n)
        (if (= n -1)
            '()
            (cons n (recur (- n 1)))))
    (recur (- n 1)))

(define (flatten a)
    (if (null? a)
        '()
        (append (car a) (flatten (cdr a)))))

(define (safe? x y sln)
    (if (null? sln)
        #t
        (let ((px (car (car sln))) (py (cadr (car sln))))
            (if (or (= y py) (= (- py y) (- px x)) (= (- py y) (- x px)))
                #f 
                (safe? x y (cdr sln))))))

(define (nqueen n)
    (define (recur x)
        (if (= x -1)
            (list '())
            (flatten (map (lambda (y) (map (lambda (sln) (cons (list x y) sln)) (filter (lambda (sln) (safe? x y sln)) (recur (- x 1))))) (range n)))))
    (recur (- n 1)))

(define (pl a)
    (if (null? a)
        '()
        (begin (display (car a)) (display "\n") (pl (cdr a)))))

(pl (nqueen 4))
+3
4

( ) . : thunk ( no-args), , , . , , . Python 3.x range() ( Python 2.x xrange()):

(define (generator start stop step)
  (let ((current (- start 1)))
    (lambda ()
      (cond ((>= current stop) #f)
            (else
             (set! current (+ current step))
             current)))))

next , , #f :

(define (next generator)
  (generator))

:

(define g (generator 0 3 1))
(next g) ; 0
(next g) ; 1
(next g) ; 2
(next g) ; 3
(next g) ; #f

, , . - Racket, sequence ( ), :

(for ([i (in-range 0 4 1)])
  (display i))

=> 0123
+3

make-iterator, guile spidermonkey (, ECMAScript 6). racket , racket call-with-continuation-prompt abort-current-continue guile call-with-prompt .

:

;; this procedure takes a generator procedure, namely a procedure
;; which has a 'yield' parameter for its first or only argument,
;; followed by such other arguments (other than the one for the
;; 'yield' parameter) as the generator procedure requires, and
;; constructs an iterator from them.  When the iterator is invoked, it
;; will begin executing the procedure unless and until the argument
;; comprising the yield procedure is called, which will cause the
;; iterator to suspend computation and instead return the value passed
;; to yield (yield is a procedure taking one argument).  If invoked
;; again, the iterator will resume computation at the point where it
;; last left off (returning a list of the values, if any, passed to
;; the iterator on resuming).  When the generator procedure has
;; executed to the end, the iterator returns 'stop-iteration.  This
;; procedure is intentionally modelled on javascript/spider-monkey
;; generators.  It has some resemblance to call/ec, except that (i)
;; instead of executing the passed procedure immediately, it returns
;; an iterator which will do so, (ii) it is resumable, and (iii) the
;; procedure to be executed can receive starting arguments in addition
;; to the yield/break argument, to provide an alternative to binding
;; them with a lambda closure.
(define (make-iterator proc . args)
  (define tag (make-prompt-tag))
  (define send-back '())
  (define (thunk)
    (apply proc
           (lambda (val)
             (abort-to-prompt tag val)
             send-back)
           args)
    ;; the generator procedure has returned - reset thunk to do
    ;; nothing except return 'stop-iteration and return
    ;; 'stop-iteration after this last call to proc
    (set! thunk (lambda () 'stop-iteration))
    'stop-iteration)
  (lambda send-args
    (set! send-back send-args)
    (call-with-prompt tag
                      thunk
                      (lambda (cont ret)
                        (set! thunk cont)
                        ret))))

:

;; for-iter iterates until the iterator passed to it (as constructed
;; by make-iterator) returns 'stop-iteration.  It invokes the procedure
;; passed as a second argument with the value yielded by the iterator
;; on each iteration.  It is mainly used for composing lazy operations
;; by pipelining, as for example with lazy-map and lazy-filter.
(define (for-iter iter proc)
  (let loop()
    (let ([val (iter)])
      (if (not (eq? val 'stop-iteration))
          (begin
            (proc val)
            (loop))))))

;; lazy-map is a procedure which takes an input iterator constructed
;; by make-iterator and a standard procedure, and then returns another
;; iterator (the output iterator) which yields the values obtained by
;; applying the standard procedure to the input iterator yielded
;; values.
(define (lazy-map iter proc)
  (make-iterator (lambda (yield)
                   (for-iter iter (lambda (val) (yield (proc val)))))))

;; lazy-filter is a procedure which takes an input iterator
;; constructed by make-iterator, and then returns another iterator
;; (the output iterator) which yields such of the values yielded by
;; the input iterator as are those for which the predicate proc
;; returns #t
(define (lazy-filter iter proc)
  (make-iterator (lambda (yield)
                   (for-iter iter (lambda (val) (if (proc val) (yield val)))))))

. 280 Rhino:

(define (counter yield initial)
  (let loop ([next-value initial])
    (let ([increment (yield next-value)])
      (if (not (null? increment))
          (if (eq? (car increment) 'reset)
              (loop initial)
              (loop (+ next-value (car increment))))
          (loop (+ 1 next-value))))))

(define counter-iter (make-iterator counter 10))   ;; create iterator at 10
(display (counter-iter))(newline)                  ;; prints 10
(display (counter-iter 2))(newline)                ;; prints 12
(display (counter-iter 'reset))(newline)           ;; prints 10

, yield , .

Edit:

, , . guile , /cc ( , ):

(define (make-iterator proc . args)
  (define prompt-cont #f)
  (define iter-cont #f)
  (define done #f)
  (define (yield arg)
    (call/cc
     (lambda (k)
       (set! iter-cont k)
       (prompt-cont arg))))
  (lambda send-back
    (if done
      'stop-iteration
      (call/cc
       (lambda (k)
         (set! prompt-cont k)
         (if iter-cont
           (iter-cont send-back)
           (begin
              (apply proc yield args)
              (set! done #t)
              (prompt-cont 'stop-iteration))))))))
+2

ChezScheme . :

(library (seq)
  (export seq hd tl range smap force-seq for)
  (import (scheme))

  (define-syntax seq
    (syntax-rules ()
      ((_ a b) (cons a (delay b)))))

  (define hd car)
  (define (tl s) (force (cdr s)))

  (define (range-impl a b s)
    (cond ((< a b) (seq a (range-impl (+ a s) b s)))
          (else    '())))


  (define (range a . b)
    (cond ((null? b)       (range-impl 0 a 1))
          ((null? (cdr b)) (range-impl a (car b) 1))
          (else            (range-impl a (car b) (cadr b)))))

  (define (smap f s)
    (cond ((null? s) '())
          (else      (seq (f (hd s)) (smap f (tl s))))))

  (define (force-seq s)
    (when (not (null? s))
      (force-seq (tl s))))

  (define-syntax for
    (syntax-rules ()
      ((_ v r body ...) (force-seq (smap (lambda (v) body ...) r)))))
)

:

(import (seq))
(for x (range 5 12)
  (display x)
  (newline))

python:

(library (io)
  (export getline lines)
  (import (scheme))
  (import (seq))

  (define (getline ip)
    (define (copy-line)
      (let ((c (get-char ip)))
        (unless (or (eof-object? c)
                    (eqv? c '#\newline))
          (display c)
          (copy-line))))
    (let ((c (peek-char ip)))
      (cond ((eof-object? c) #f)
            (else (with-output-to-string copy-line)))))


    (define (lines ip)
      (let ((l (getline ip)))
        (cond (l    (seq l (lines ip)))
              (else '()))))
)

:

(import (seq))
(import (io))

(for l (lines (current-input-port))
  (display l)
  (newline))
+2

, do , :

(do ((i 0 (add1 i)))
  ((= i 100000) 'result)
   (some-function! i some-data))

, Racket in-list for, .

(for/list ((i (in-list (range 0 100000))))
  (some-function i some-data))
+1

All Articles