可以按照你的意愿创建列表,只要你set!append 返回的值(记住:append不就地修改列表,它创建一个必须存储在某处的新列表)并在最后调用 write:
(define (divisor N)
(define L '())
(for ([i (in-range 1 N)]) ; generate a stream in the range 1..N
(when (zero? (modulo N i)) ; use `when` if there's no `else` part, and `zero?`
; instead of `(equal? x 0)`
(set! L (append L (list i))) ; `set!` for updating the result
(write L) ; call `write` like this
(newline)))) ; insert new line
…但这不是在 Scheme 中做事的惯用方式,尤其是在 Racket 中:
- 我们尽可能避免像
set!这样的变异操作
- 在循环中
write 是个坏主意,您会在控制台中打印出大量文本
- 不建议将
append 元素放在列表末尾,这将花费二次时间。我们更喜欢使用cons 在列表头部添加新元素,如果需要,在末尾反转列表
考虑到上述所有因素,这就是我们如何在 Racket 中实现更惯用的解决方案 - 使用 stream-filter,不打印,也不使用 for 循环:
(define (divisor N)
(stream->list ; convert stream into a list
(stream-filter ; filter values
(lambda (i) (zero? (modulo N i))) ; that meet a given condition
(in-range 1 N)))) ; generate a stream in the range 1..N
另一个选项(类似于@uselpa 的答案),这次使用for/fold 进行迭代和累积值 - 再次,无需打印:
(define (divisor N)
(reverse ; reverse the result at the end
(for/fold ([acc '()]) ; `for/fold` to traverse input and build output in `acc`
([i (in-range 1 N)]) ; a stream in the range 1..N
(if (zero? (modulo N i)) ; if the condition holds
(cons i acc) ; add element at the head of accumulator
acc)))) ; otherwise leave accumulator alone
无论如何,如果需要打印中间的所有步骤,这是一种方法 - 但效率低于以前的版本:
(define (divisor N)
(reverse
(for/fold ([acc '()])
([i (in-range 1 N)])
(if (zero? (modulo N i))
(let ([ans (cons i acc)])
; inefficient, but prints results in ascending order
; also, `(displayln x)` is shorter than `(write x) (newline)`
(displayln (reverse ans))
ans)
acc))))