Clojure在“普通口号"中等同于Clojure的“关联"和“获取" [英] Equivalent of Clojure's “assoc-in” and “get-in” in Common lisp

查看:69
本文介绍了Clojure在“普通口号"中等同于Clojure的“关联"和“获取"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Clojure中,您可以使用assoc-in更新地图(字典)并自动创建密钥路径(如果不存在).

In Clojure you can update a map (dict) with assoc-in and create key path automatically if it don't exist.

(assoc-in {:a 1 :b 3} [:c :d] 33)
; {:a 1, :c {:d 33}, :b 3}

与获取相同:您可以指定键(或列表索引)的路径,它将返回该路径指定的值,如果不存在则返回nil.

Same for get-in: you can specify a path of keys (or list indices) and it will return the value specified by the path, nil if it does not exist.

(get-in {:a 1, :c {:d 33}, :b 3} [:c :d])
; 33
(get-in {:a 1, :c {:d 33}, :b 3} [:c :e])
; nil

我喜欢把糖放在Common Lisp中,所以我胡闹了一个'assoc-in',然后我用列表结构制成的特里测试了它,然后保留了':leaf',所以得到'get'的结果-in'始终是列表:

I like to have the sugar in Common lisp,so I monkeyed a 'assoc-in' and I tested it on a trie I made out of list structure, I leave ':leaf' preserved, so the result of 'get-in' is always list:

  • 测试案例:(setf trie'(:A(:B(:leaf t):C(:leaf t)):D(:leaf t)))

入门实施和测试:

(defmacro get-in (trie ks)  `(reduce #'getf  ,ks :initial-value ,trie))
(get-in trie '(:a :b)) ; (:leaf t)
(get-in trie '(:a :b :leaf)) ; t

关联的实现和测试:

(defmacro assoc-in (trie ks v)
 `(setf (getf (get-in ,trie ,ks) :leaf) ,v))

(assoc-in trie '(:a :b) 99)
(get-in trie '(:a :b)) ; (:leaf 99)
(assoc-in trie '(:a :b :new-key) "new-key") ; (SETF REDUCE) is not fbound

我在使用关联"时遇到麻烦,我可以更新Trie但不能插入

I have trouble with 'assoc-in', I can update the trie but can't insert

任何建议都受到欢迎,不必一定是宏.我查找了 Clojure实现并尝试在Common Lisp中执行此操作,也失败了.

Any advice is welcomed, doesn't have to be macro. I looked up Clojure implementation and tried to do it in Common lisp, also failed.

推荐答案

这是我要执行的操作.代码中的文档字符串和注释说明了正在发生的事情.首先是一个实用程序功能:

Here's how I'd do this. The docstrings and comments in the code explain what's happening. First a utility function:

(defun make-nested-alist (value items)
  "Returns a nested association list that takes the first item
to an association list that takes the second item to an 
association list, and so on, finally to the value.  If items
is the empty list, then the value is returned by itself."
  (reduce (lambda (item value)
            (acons item value '()))
          items :initial-value value
          :from-end t))

(make-nested-alist 3 '())
;=> 3
(make-nested-alist 3 '(a b c))
;;=> ((a . ((b . ((c . e))))))

现在,该函数将从嵌套关联列表中检索值.

Now, a function that will retrieve values from a nested association list.

(defun assoc* (items alist)
  "Finds the item in the nested association list keyed by the items.
It is an error if a prefix of the path leads to a value that cannot be
interpreted as an associate list."
  (if (endp items) alist
      (assoc* (rest items)
              (cdr (assoc (first items) alist)))))

(defparameter *alist*
  (copy-tree '((a . 1)
               (b . 3)
               (c . ((d . 33))))))

(assoc* '(a) *alist*) ;=> 1
(assoc* '(c d) *alist*) ;=> 33
(assoc* '(c e) *alist*) ;=> NIL
(assoc* '(a b) *alist*) ; ERROR (since the prefix (a) leads to 1)

现在,一个用于更新"嵌套关联列表的函数.请注意,这会更新到位的大多数关联列表,但是由于无法修改到位的空列表,因此需要使用此函数的返回值.

Now, a function to "update" a nested association list. Note that this will update most association lists in place, but since you can't modify an empty list in place, you need to use the return value from this function.

(defun assoc*-update (value items alist)
  "Returns a nested association list like the provided one, except
that the value at the path specified by items contains value.  This
will modify the association list if the any prefix of the path is a
value in the association list.  Because the result may be a new
list (e.g., when the original association list does not have a top
level entry for the initial item in the path), the result should be
used."
  (if (endp items)
      value
      (let ((entry (assoc (first items) alist)))
        (if (null entry)
            ;; if there is no entry at all, then we need to make a
            ;; nested association list out of the rest keys that
            ;; eventually comes back to the value, and tack it onto
            ;; the current alist. We can't modify alist, because alist
            ;; might be empty, and we can't modify the empty list.
            (acons (first items) (make-nested-alist value (rest items)) alist)
            ;; Otherwise, there is an entry, and that takes care of the
            ;; first key, but we'll need to recurse into the value and
            ;; update it.  If there are no keys left, then we should just
            ;; replace this entry, otherwise we need to recurse into it.
            ;; In both cases, we return alist.
            (prog1 alist
              (rplacd entry (assoc*-update value (rest items) (cdr entry))))))))

(let ((alist (copy-tree *alist*)))
  (setf alist (assoc*-update 42 '(c e) alist))
  (print alist)
  (setf alist (assoc*-update 89 '(d) alist))
  (print alist))
;=>          ((A . 1) (B . 3) (C (E . 42) (D . 33))) 
;=> ((D . 89) (A . 1) (B . 3) (C (E . 42) (D . 33))) 

这篇关于Clojure在“普通口号"中等同于Clojure的“关联"和“获取"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆