我在球拍中运行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)
提前多多感谢!
答案 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
。
有很多方法可以表达这一点。我认为我们所有答案的共同点是您可能希望避免使用for
和set!
来构建结果列表。这样做不是惯用的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
的值始终为()
。原因是你永远不会改变L
。 append
的作用是返回一个新列表 - 它不会影响其任何参数的值。因此,在不使用其返回值的情况下使用它不会有任何用处。
如果您想要进行循环工作,则需要使用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))))
在输出之前反转,因为项目已添加到列表的头部(带缺点)。