不使用 set 更新列表! - 方案

Updating a list without using set! - Scheme

关于在不使用 set 的情况下在内存中保留列表的问题!

我定义了一个初始空列表,

(define database (list))

然后我有这个程序检查密码是否正确并将密码对添加到列表中。

(define (set-pass l)
  (if (pair? l)
    (if (check-pass (second (last l)))
      (add-to-list l) 
      "password does not meet policy requirements"
      )
  "invalid input"
  )
)

以及添加到列表的过程:

(define (add-to-list l)
  ;(append database l)
  ;implement this.
  )

问题是,我必须多次调用此过程:

(set-pass '('john '(X p c F z C b Y h 1 2 3 4 : :)))
(set-pass '('john '(X p c F z C b Y : 1 2 3 4 : :)))
(set-pass '('john '(X p c F z C b : : 1 2 3 4 : :)))

我实现了 add-to-list 过程,就像我调用一次 set-pass 一样(如上所示带有 append),但是如果我多次调用它,我找不到实现的方法。我尝试了提到的一些事情 here, here and here。但是我无法实现我想要的。那我该怎么做呢?

可以通过将数据库作为变量来在功能上做到这一点:

(let loop ((input (read-line)) (database '()))
  (display (format "inserting ~a\n" input))
  (loop (read-line)
        (cons input database)))

其他功能(删除等)的工作方式与您根据操作重复使用更改后的结构的方式相同。

您还可以使用 set-cdr! 更新列表。 set! 改变符号指向的内容,而 set-cdr! 改变一对中的 cdr。因为它需要是一对,所以你需要让第一个元素是一些虚拟数据:

(define database (list "head"))

(define (add element)
  (let ((tmp (cdr database)))
    (set-cdr! database (cons element tmp))))

(define (delete element)
  (let loop ((prev database) (cur (cdr database)))
    (cond ((null? cur) #f)
          ((equal? (car cur) element) 
           (set-cdr! prev (cdr cur)))
          (else (loop cur (cdr cur))))))

(define (get)
  (cdr database))

(add 1)
(add 2)
(add 3)
(get) ; ==> (3 2 1)
(delete 2)
(get) ; ==> (3 1)

第二次允许突变时,猫就出局了,所有突变都可用。例如。如果提供了 set!,你可以创建一个带有闭包的可变对象,如果提供了 set-car!/set-cdr!,你可以通过框获得可变绑定。