在Racket中做两件事“for loop”

时间:2014-09-07 14:13:43

标签: for-loop racket

我在球拍中运行for循环,对于列表中的每个对象,我想执行两件事:如果项目满足条件,(1)将其附加到我的新列表中,(2)然后打印名单。但我不确定如何在Racket中这样做。

这是我的除数函数:在if语句中,我检查范围内的数字是否可以除N.如果是,我将该项追加到我的新列表L中。完成所有循环后,我打印L.但由于某些未知原因,该函数仍将L作为空列表返回,因此我想看看for循环在每个循环中的作用。但显然球拍似乎并没有在一个“for循环”中采取两个动作。那我该怎么做呢?

    (define (divisor N)
       (define L '())
      (for ([i (in-range 1 N)])
         (if (equal? (modulo N i) 0)
             (append L (list i))
             L)
           )
       write L)

提前多多感谢!

5 个答案:

答案 0 :(得分:2)

注意:这个答案建立在@uselpa的答案的基础上,我赞成了。

for表单有一个可选的#:when子句。使用for/fold

#lang racket

(define (divisors N)
  (reverse (for/fold ([xs '()])
                     ([n (in-range 1 N)]
                      #:when (zero? (modulo N n)))
             (displayln n)
             (cons n xs))))

(require rackunit)
(check-equal? (divisors 100)
              '(1 2 4 5 10 20 25 50))

我意识到你的核心问题是关于如何显示每个中间列表。但是,如果您需要这样做,那么使用for/list会更简单:

 (define (divisors N)
  (for/list ([n (in-range 1 N)]
             #:when (zero? (modulo N n)))
    n))

换句话说,传统的方案(filter __ (map __))filter-map也可以使用for/list条款在Racket中表示为#:when


有很多方法可以表达这一点。我认为我们所有答案的共同点是您可能希望避免使用forset!来构建结果列表。这样做不是惯用的Scheme或Racket。

答案 1 :(得分:1)

@ sepp2k已经回答了您的问题,为什么您的结果始终为null

在Racket中,更惯用的方法是使用for/fold:  

(define (divisor N)
  (for/fold ((res null)) ((i (in-range 1 N)))
    (if (zero? (modulo N i))
        (let ((newres (append res (list i))))
          (displayln newres)
          newres)
        res)))

测试:

> (divisor 100)
(1)
(1 2)
(1 2 4)
(1 2 4 5)
(1 2 4 5 10)
(1 2 4 5 10 20)
(1 2 4 5 10 20 25)
(1 2 4 5 10 20 25 50)
'(1 2 4 5 10 20 25 50)

由于append效果不佳,您通常会使用cons,最后会得到reverse所需的列表:

(define (divisor N)
  (reverse
   (for/fold ((res null)) ((i (in-range 1 N)))
     (if (zero? (modulo N i))
         (let ((newres (cons i res)))
           (displayln newres)
           newres)
         res))))

> (divisor 100)
(1)
(2 1)
(4 2 1)
(5 4 2 1)
(10 5 4 2 1)
(20 10 5 4 2 1)
(25 20 10 5 4 2 1)
(50 25 20 10 5 4 2 1)
'(1 2 4 5 10 20 25 50)

答案 2 :(得分:1)

可以按照您的意图创建列表,只要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))))

答案 3 :(得分:0)

要在Racket的for循环中执行多项操作,您只需在彼此之后编写它们即可。因此,要在每次迭代后显示L,您可以这样做:

(define (divisor N)
  (define L '())
  (for ([i (in-range 1 N)])
    (if (equal? (modulo N i) 0)
        (append L (list i))
        L)
    (write L))
  L)

请注意,您需要使用括号来调用函数,因此它(write L) - 而不是write L。我还用write L替换了for循环之外的L,因为你(大概)想要从最后的函数返回L - 不打印它(因为你没有& #39;周围有括号,无论如何都是这样做的。)

这将告诉您的是L的值始终为()。原因是你永远不会改变Lappend的作用是返回一个新列表 - 它不会影响其任何参数的值。因此,在不使用其返回值的情况下使用它不会有任何用处。

如果您想要进行循环工作,则需要使用set!来实际更改L的值。然而,避免变异会更加惯用,而是使用filter或递归来解决这个问题。

答案 4 :(得分:0)

'命名为let',一般方法,可以在这里使用:

(define (divisors N)
  (let loop ((n 1)                ; start with 1
             (ol '()))            ; initial outlist is empty;
    (if (< n N)
        (if(= 0 (modulo N n))
           (loop (add1 n) (cons n ol))  ; add n to list
           (loop (add1 n) ol)           ; next loop without adding n
           )
        (reverse ol))))           

在输出之前反转,因为项目已添加到列表的头部(带缺点)。