Common lisp 使用setf避免冗余位置计算 上下文

Common lisp 使用setf避免冗余位置计算 上下文,common-lisp,Common Lisp,我在使用CommonLisp的有限经验中发现,有这样的代码并不少见 (setf (gethash key table) (my-transformation (gethash key table))) 在从setfable位置读取数据的地方,对存储在那里的值执行一些计算,然后写入相同的位置。我不喜欢的是,这个位置会被计算两次,但(希望!)两次都是一样的。如果位置计算是昂贵的,我们所做的工作是我们需要的两倍 问题 是否有可能消除双重计算的需要?也就是说,是否可能(需要?)在原地编写宏

我在使用CommonLisp的有限经验中发现,有这样的代码并不少见

(setf (gethash key table)
      (my-transformation (gethash key table)))
在从
setf
able位置读取数据的地方,对存储在那里的值执行一些计算,然后写入相同的位置。我不喜欢的是,这个位置会被计算两次,但(希望!)两次都是一样的。如果位置计算是昂贵的,我们所做的工作是我们需要的两倍

问题 是否有可能消除双重计算的需要?也就是说,是否可能(需要?)在原地编写宏
setf

(setf-inplace (gethash key table) #'my-transformation)
在概念上等同于(暂时忽略多个值古怪),但可能比原始代码快得多,最好不依赖实现细节

我知道在这种特殊情况下,我可能会把车放在马的前面,因为,但在我看来,其他
setf
可缓存的位置,比如
(assoc key my alist)
,可能不太容易缓存,当然,除非通过上面的
setf inplace
之类的机制

(setf (gethash key table)
      (my-transformation (gethash key table)))
假设转换为
1+
。那么上面是

(setf (gethash key table)
      (1+ (gethash key table)))
在公共Lisp中,哪个有宏:

(incf (gethash key table))
Clozure CL将其扩展为:

(LET* ((#:G69020 KEY)
       (#:G69021 TABLE)
       (#:G69022 1)
       (#:G69019 (+ (GETHASH #:G69020 #:G69021) #:G69022)))
  (DECLARE (TYPE BIT #:G69022) (TYPE T #:G69019))
  (CCL::PUTHASH #:G69020 #:G69021 #:G69019))
现在请记住,位置的setter是在编译时找到的,并且位置的setter操作与getter不同。setter不仅可以通过更新某些内容来改变某些内容,还可以执行任意其他操作,如设置事务、获取锁等。。。因此,获取和设置不是两次相同/相似的操作

Common Lisp没有表示位置的物理数据结构。地点是一个抽象的概念,它结合了一个设定者和一个给定的获取者。setter可以是任意复杂代码

那么,在上面的代码中,哪些计算是多余的

您仍然会说,如何优化基本情况的访问:

(incf (car (very-long-access-chain data)))
你必须手写:

(let ((cons-cell (very-long-access-chain data)))
  (incf (car cons-cell))

基本上Lisp就是这么做的。请参阅:CLHS

我没有看到将很长的访问链从incf移动到let的任何好处。在上一个示例中,在我(和我的宏扩展器)看来,这两种形式之间的唯一区别是额外的词汇绑定。我是否没有抓住要点?这是有道理的;我没有意识到getter和setter可能是不相关的计算。事实上,看看SBCL的
SB!IMPL:GETHASH
等,实现之间共享的代码不多。然而,在我看来,它应该是可能的,例如,定义一个setf扩展器,它将“位置”缓存在它的临时变量中。在这种情况下,像
setf inplace
这样的宏应该是可行的。不过,我不知道是否有可能用非缓存的setf扩展器编写它来做任何合理的事情。@stuartolsen:你找不到一个地方。它不存在。位置不是可以存储的指针或数据结构。这就是问题所在。@Stuart Olsen:pair不是存储引用所必需的结构<代码>(cons 3无)。这三个不是一个参考<代码>(let((foo some object))(list(cons foo nil)(cons foo nil)))。如果某个对象是fixnum,它将以内联方式存储。如果某个对象是数组,则将存储一个引用,并且两个引用将指向同一个对象。cons也不能指向哈希表、复数、向量、字符串或抽象对象……setf机制很酷,但我认为它不能满足OP的要求。考虑(CIF(GETF:关键位置0))。据我所知,为getf编写setf方法是不可能的,这样就可以避免在值中搜索属性列表两次。同时,setf不是线程安全的,所以虽然这很有趣,但它会分散注意力。