【问题标题】:Dynamically fill a list with key value pairs用键值对动态填充列表
【发布时间】:2014-05-14 12:00:38
【问题描述】:

我需要用对填充一个列表,例如:((x 10)(z 5))。

我目前的方法:

;;send the current list, and an id value pair
(define (setID idList ID VAL)

  (cond 
        ;;if the list is null, add the pair
        ((null? idList) (cons '(ID VAL) '()))    

        ;; if the ID already exists, overwrite it
        ((equal? ID (car(car idList)))  (cons '((cdr idList)) VAL))

        ;; continue the search
        (else  (setID (cdr idList) ID VAL))
  )
)

我意识到我还需要使用cons 来保持列表的完整性,但第一个问题是当我执行(setID z 5) 之类的操作时,返回的列表正是:((id val))。显然,它需要是((z 10))。有没有办法做这样的事情?

【问题讨论】:

    标签: recursion scheme racket r5rs


    【解决方案1】:

    这是来自@WorBlux 的答案的 Racket-y 版本:

    您的“setID”本质上已经在 Racket 中以名称 dict-set 定义。

    (dict-set dict key v) → (and/c dict? immutable?)
       dict : (and/c dict? immutable?)
       key  : any/c
       v    : any/c
    

    通过将key 映射到v 来功能扩展dict,覆盖key 的任何现有映射,并返回扩展字典。如果dict 不支持功能扩展或者key 不是字典的允许键,则更新可能会失败并出现exn:fail:contract 异常。

    例子:

    > (dict-set #hash() 'a "apple")
    '#hash((a . "apple"))
    
    > (dict-set #hash((a . "apple") (b . "beer")) 'b "banana")
    '#hash((b . "banana") (a . "apple"))
    
    > (dict-set '() 'a "apple")
    '((a . "apple"))
    
    > (dict-set '((a . "apple") (b . "beer")) 'b "banana")
    '((a . "apple") (b . "banana"))
    

    请参阅上面的最后两个示例。

    当然,您可以使用dict-ref 和其他函数通过键查找值,或映射它们,等等。

    请注意,dict-set 与您所描述的略有不同,因为assoc(由dict 使用)使用(key . val) 而不是(key val)。换句话说,它使用(cons key val) 而不是(list key val)。使用(cons a b) 存储两个项目比使用(cons a (cons b '())) 更有效。

    【讨论】:

      【解决方案2】:

      不要重新发明轮子,关联列表在方案中很常见

      这里最简单的是带有头部标签的守卫。这样((x 10)(z 5)) 就变成了(*idList* (x 10)(z 5))

      ;;send the current list, and an id value pair
      (define (setID! idList ID VAL)
      ;;if a function mutates data end it with a bang
        (let ((ID-VAL (assoc ID (cdr idList)))) 
              ;;assoc returns #f if no match, with the ID-VAL pair if there is a match
              (if ID-VAL
                  (set-car! (cdr ID-VAL) VAL)  
                           ;; if the ID already exists, overwrite it     
                  (set-cdr! idList (cons (list ID VAL) (cdr idList)))))
                           ;;if ID into in assoc-list, add the pair
          idList)      ;;return the idList
      

      测试

      (setID! '(*idList*) 'x 10)
      ;Value 3: (*idlist* (x 10))
      
      (setID! '(*idlist* (x 10)) 'y 20)
      ;Value 4: (*idlist* (y 20) (x 10))
      
      (setID! '(*idlist* (y 20) (x 10)) 'x 30)
      ;Value 9: (*idlist* (y 20) (x 30))
      

      【讨论】:

      • 我目前的挑战不允许我使用大部分代码,但它仍然是一个很好的答案,抱歉我之前没有指定。谢谢。
      【解决方案3】:

      您的代码存在三个主要问题:

      • 在这里:(cons '(ID VAL) '())) 你正在构建一个新的对,其值为(ID VAL) - 也就是说,第一个元素符号ID,第二个元素是 符号VAL。这不是你想要的,你想要IDvalueVALvalue。确保您了解 quote 的工作方式
      • 当您找到现有的 ID 时,您必须将新修改的对与列表的其余部分结合起来
      • 即使当前对不是我们正在寻找的那对,我们必须cons它到输出。请记住:我们在遍历列表时正在构建答案

      这就是我的意思:

      ;;send the current list, and an id value pair
      (define (setID idList ID VAL)
        (cond 
          ;;if the list is null, add the pair
          ((null? idList) (cons (list ID VAL) '()))
          ;; if the ID already exists, overwrite it
          ((equal? ID (car (car idList))) (cons (list ID VAL) (cdr idList)))
          ;; continue the search
          (else (cons (car idList) (setID (cdr idList) ID VAL)))))
      

      并且不要忘记执行此过程后返回的列表是一个列表,如果要继续向其中添加元素,则必须将其存储在某处或将其作为参数传递- 因为最初作为参数接收的列表保持不变。现在程序按预期工作:

      (setID '() 'x 10)
      => '((x 10))
      
      (setID '((x 10)) 'y 20)
      => '((x 10) (y 20))
      
      (setID '((x 10) (y 20)) 'x 30)
      => '((x 30) (y 20))
      

      【讨论】:

      • 谢谢。你。非常。只需将其更改为您所拥有的,整个程序就开始正常工作。以为我还有很长的路要走。
      • 很高兴听到这个消息! :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-26
      • 1970-01-01
      • 2013-08-02
      • 2011-01-13
      • 2012-02-25
      • 2011-05-01
      • 1970-01-01
      相关资源
      最近更新 更多