今天我使用Scheme解决了N-queen问题,但与相同版本的Python相比,它非常慢。当N = 8时,Scheme需要90秒以上!我知道一个原因是我不能在Scheme中使用生成器,我的代码必须首先形成大型列表,这是内存和计算的噩梦。
在Scheme中关于生成器的主题很少,this one是我发现的唯一可能有用但可悲的是它在球拍或chez方案中都不起作用。
实际上,我只想要一个简单版本的python生成器,也就是说,不要形成整个列表,只需一次向我发送一个值。即:
(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 Scheme代码:
(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))
答案 0 :(得分:4)
对于这种情况使用延续(如链接中所示)是不合理的。这里有一个更简单的想法:让我们将我们的生成器定义为thunk(一个无参数函数),它将环境的一部分,最大允许值,增量和当前元素存储为其环境的一部分。每次调用该过程时,都会更新当前元素。以下代码的行为类似于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
另一种选择是使用流,但我会坚持使用上面的解决方案,它很简单,应该适用于任何Scheme解释器。还有另一种选择 - 如果您在Racket中编码,只需使用sequence(也是一个流),如下所示:
(for ([i (in-range 0 4 1)])
(display i))
=> 0123
答案 1 :(得分:2)
我有一个make-iterator程序,使用guile提示来实现spidermonkey生成器(类似但不同于ECMAScript 6生成器)。由于球拍也有提示,这应该可以直接转换为球拍的呼叫 - 继续 - 提示和中止 - 当前 - 继续,而不是guile的呼叫 - 提示和中止 - 提示。
以下是代码:
;; 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's 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)))))))
以下是第6版犀牛书第280页的规范反例:
(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 keyname注入代码体,但我更喜欢上面的方法。
修改强>
对于不支持提示的方案实施,以下内容与使用提示的版本完全相同。但是对于guile,提示比使用完整的call / cc continuation更有效(我猜对于所有实现都不一定如此):
(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 :(得分: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))
答案 3 :(得分:1)