根据 Racket Advanced Student 中另一个列表的位置编号制作列表
Making a list from the position number of another list in Racket Advanced Student
我目前正在尝试编写一个函数,该函数为我提供一个列表,其中包含包含元素 1 作为元素的位置的位置编号。
不幸的是,当我执行以下函数时,它给了我 '()
,这是 lst
的初始值。
所以我不确定要为案例写什么(< pos 0)
。
(define lst '())
(define (posj pos) (if (< pos 0)
lst
(begin (cond ((equal? (list-ref l pos) 1) (begin (set! lst (append (list* pos) '()))
(posj (- pos 1))))
(else (posj (- pos 1)))))))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
您的代码不起作用(追加仅需要列表)并且出于多种原因不是在 Scheme 中执行此操作的方法。
我建议使用 named let(经典方案):
(define (posj lst val)
(let loop ((lst lst) (pos 0) (res null))
(if (null? lst)
(reverse res)
(loop (cdr lst)
(add1 pos)
(if (= (car lst) val) (cons pos res) res)))))
或更多"rackety"
(define (posj lst val)
(reverse
(for/fold ((res null)) (((elt pos) (in-indexed lst)))
(if (= elt val) (cons pos res) res))))
然后
> (posj '(1 3 1 2 1 5 1) 1)
'(0 2 4 6)
由于您使用的是 Racket 并且可能是 Dr Racket(IDE),
- 使用自动缩进功能,它有助于提高代码的可读性
- 为了理解这段代码是如何工作的,使用内置调试器逐步执行它
- 阅读 Scheme 教程,其中将教您常见的循环结构以及其他有用的东西。
你可能想写的是这个,用 list
代替 list*
,用 lst
代替 '()
:(并且有更标准的缩进)
(define lst '())
(define (posj pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(begin
(set! lst (append (list pos) lst))
(posj (- pos 1)))]
[else
(posj (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
这可行,但它依赖于全局变量(l
和 lst
),这不是一个很好的做事方式。然后您可以将其从全局变量转换为函数参数。 (set! lst (append ...))
可以通过将 (append ...)
作为参数传递来替换:
(define (posj l lst pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(posj l (append (list pos) lst) (- pos 1))]
[else
(posj l lst (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() (- (length l) 1))
到目前为止l
每次都是同一个列表,当你真正需要的部分每次都缩小时。要解决此问题,您可以从前到后而不是从后到前进行迭代,并在递归中使用 first
和 rest
。此外,append
的参数顺序必须切换,因为我们现在在另一个方向迭代,(< pos 0)
检查可以替换为 (empty? l)
检查:
(define (posj l lst pos)
(if (empty? l)
lst
(cond [(equal? (first l) 1)
(posj (rest l) (append lst (list pos)) (+ pos 1))]
[else
(posj (rest l) lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
现在,如果您可以使用 cons
而不是使用 append
,效率会高得多。这会反转 lst
,因此将其重命名为 rev-lst
,当您 return 它时,再次反转它:
(define (posj l rev-lst pos)
(if (empty? l)
(reverse rev-lst)
(cond [(equal? (first l) 1)
(posj (rest l) (cons pos rev-lst) (+ pos 1))]
[else
(posj (rest l) rev-lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
现在它开始看起来很像列表函数的模板,尤其是当您将 if
替换为 cond
时。此外,如果您想避免将 '()
和 0
作为额外参数传递,您可以使用辅助函数或命名 let.
我目前正在尝试编写一个函数,该函数为我提供一个列表,其中包含包含元素 1 作为元素的位置的位置编号。
不幸的是,当我执行以下函数时,它给了我 '()
,这是 lst
的初始值。
所以我不确定要为案例写什么(< pos 0)
。
(define lst '())
(define (posj pos) (if (< pos 0)
lst
(begin (cond ((equal? (list-ref l pos) 1) (begin (set! lst (append (list* pos) '()))
(posj (- pos 1))))
(else (posj (- pos 1)))))))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
您的代码不起作用(追加仅需要列表)并且出于多种原因不是在 Scheme 中执行此操作的方法。
我建议使用 named let(经典方案):
(define (posj lst val)
(let loop ((lst lst) (pos 0) (res null))
(if (null? lst)
(reverse res)
(loop (cdr lst)
(add1 pos)
(if (= (car lst) val) (cons pos res) res)))))
或更多"rackety"
(define (posj lst val)
(reverse
(for/fold ((res null)) (((elt pos) (in-indexed lst)))
(if (= elt val) (cons pos res) res))))
然后
> (posj '(1 3 1 2 1 5 1) 1)
'(0 2 4 6)
由于您使用的是 Racket 并且可能是 Dr Racket(IDE),
- 使用自动缩进功能,它有助于提高代码的可读性
- 为了理解这段代码是如何工作的,使用内置调试器逐步执行它
- 阅读 Scheme 教程,其中将教您常见的循环结构以及其他有用的东西。
你可能想写的是这个,用 list
代替 list*
,用 lst
代替 '()
:(并且有更标准的缩进)
(define lst '())
(define (posj pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(begin
(set! lst (append (list pos) lst))
(posj (- pos 1)))]
[else
(posj (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
这可行,但它依赖于全局变量(l
和 lst
),这不是一个很好的做事方式。然后您可以将其从全局变量转换为函数参数。 (set! lst (append ...))
可以通过将 (append ...)
作为参数传递来替换:
(define (posj l lst pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(posj l (append (list pos) lst) (- pos 1))]
[else
(posj l lst (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() (- (length l) 1))
到目前为止l
每次都是同一个列表,当你真正需要的部分每次都缩小时。要解决此问题,您可以从前到后而不是从后到前进行迭代,并在递归中使用 first
和 rest
。此外,append
的参数顺序必须切换,因为我们现在在另一个方向迭代,(< pos 0)
检查可以替换为 (empty? l)
检查:
(define (posj l lst pos)
(if (empty? l)
lst
(cond [(equal? (first l) 1)
(posj (rest l) (append lst (list pos)) (+ pos 1))]
[else
(posj (rest l) lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
现在,如果您可以使用 cons
而不是使用 append
,效率会高得多。这会反转 lst
,因此将其重命名为 rev-lst
,当您 return 它时,再次反转它:
(define (posj l rev-lst pos)
(if (empty? l)
(reverse rev-lst)
(cond [(equal? (first l) 1)
(posj (rest l) (cons pos rev-lst) (+ pos 1))]
[else
(posj (rest l) rev-lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
现在它开始看起来很像列表函数的模板,尤其是当您将 if
替换为 cond
时。此外,如果您想避免将 '()
和 0
作为额外参数传递,您可以使用辅助函数或命名 let.