根据 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),

  1. 使用自动缩进功能,它有助于提高代码的可读性
  2. 为了理解这段代码是如何工作的,使用内置调试器逐步执行它
  3. 阅读 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))

这可行,但它依赖于全局变量(llst),这不是一个很好的做事方式。然后您可以将其从全局变量转换为函数参数。 (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每次都是同一个列表,当你真正需要的部分每次都缩小时。要解决此问题,您可以从前到后而不是从后到前进行迭代,并在递归中使用 firstrest。此外,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.