Common lisp setf表格的评估

Common lisp setf表格的评估,common-lisp,expression-evaluation,setf,Common Lisp,Expression Evaluation,Setf,这个问题是关于公共Lispsetf宏,以及它如何计算其参数形式(和子形式)——也就是说,如果它们碰巧出现多次,则只计算一次。(这也是对评论中给出的示例的部分后续。) ;创建两个哈希表的列表 *(定义参数hts(列表(生成哈希表)(生成哈希表))) 高温超导 *高温超导 (# #) ;定义一个交换两个哈希表位置的函数 *(卸载下一个ht(hts) (rotatef(第一hts)(第二hts)) (第二个hts) 下一代 交换: ;now do a swap to verify it works

这个问题是关于公共Lisp
setf
宏,以及它如何计算其参数形式(和子形式)——也就是说,如果它们碰巧出现多次,则只计算一次。(这也是对评论中给出的示例的部分后续。)

;创建两个哈希表的列表
*(定义参数hts(列表(生成哈希表)(生成哈希表)))
高温超导
*高温超导
(#
#)
;定义一个交换两个哈希表位置的函数
*(卸载下一个ht(hts)
(rotatef(第一hts)(第二hts))
(第二个hts)
下一代
交换:

;now do a swap to verify it works
* (next-ht hts)
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}>
* hts
(#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}>
 #<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}>)

;and swap them back
* (next-ht hts)
#<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}>
* hts
(#<HASH-TABLE :TEST EQL :COUNT 0 {1007F76CB3}>
 #<HASH-TABLE :TEST EQL :COUNT 0 {1007F77103}>)
;现在做一次交换来验证它是否有效
*(下一个ht hts)
#
*高温超导
(#
#)
;然后把它们换回来
*(下一个ht hts)
#
*高温超导
(#
#)
进一步测试:

;then set different values for a key in each table
* (setf (gethash 0 (first hts)) 11)
11
* (setf (gethash 0 (second hts)) 22)
22
* hts
(#<HASH-TABLE :TEST EQL :COUNT 1 {1007F76CB3}>
 #<HASH-TABLE :TEST EQL :COUNT 1 {1007F77103}>)

;finally execute a setf with a swapping side-effect
* (setf (gethash 0 (next-ht hts)) (1+ (gethash 0 (next-ht hts))))
23

;but it looks like hts has been swapped twice
;back to its original state
* hts
(#<HASH-TABLE :TEST EQL :COUNT 1 {1007F76CB3}>
 #<HASH-TABLE :TEST EQL :COUNT 1 {1007F77103}>)

;also, where did the initial value of 11 go?
* (gethash 0 (first hts))
23
T
* (gethash 0 (second hts))
22
T
*
;然后为每个表中的键设置不同的值
*(setf(gethash 0(第一个hts))11)
11
*(setf(gethash 0(第二个hts))22)
22
*高温超导
(#
#)
;最后执行一个带有交换副作用的setf
*(setf(gethash 0(下一个ht hts))(1+(gethash 0(下一个ht hts)))
23
;但看起来hts已经换了两次了
;恢复原状
*高温超导
(#
#)
;还有,11的初始值到哪里去了?
*(gethash 0(第一个hts))
23
T
*(gethash 0(第二个hts))
22
T
*

有人能澄清发生了什么事吗?另外,有副作用的
setf
表达式是什么意思?

为什么不宏扩展
setf
表单?这里是LispWorks:

CL-USER 32 > (pprint (macroexpand '(setf (gethash 0 (next-ht hts))
                                         (1+ (gethash 0 (next-ht hts))))))

(LET* ((#:|key1014| 0)
       (#:|table1015| (NEXT-HT HTS))
       (#:|default1016| NIL)
       (#:|store1017| (1+ (GETHASH 0 (NEXT-HT HTS)))))
  (SYSTEM::%PUTHASH #:|key1014| #:|table1015| #:|store1017|))
它有什么作用

  • 获取键值
  • 获取哈希表,调用
    NEXT-HT
  • 获取默认值,不使用
  • 获取新值,调用
    NEXT-HT
  • 使用特定于实现的方式将新的键/值存储到哈希表中
因此,显然
NEXT-HT
被调用了两次

它背后的粗略(!)概念模型是什么

  • setf将检查第一个表达式
  • 这是什么?哦,它是
    gethash
    ,让我为它设置setter表单
  • 然后,setter表单将从第一个表单计算必要的子表单
  • 然后它将计算新值
  • 将使用这些参数调用setter运算符
例如:

CL-USER 62 > (setf (gethash (print 0)
                            (print (next-ht hts))
                            (print 1))
                   (print (1+ (print (gethash 0
                                              (print (next-ht hts))
                                              2)))))

0 
#<EQL Hash Table{1} 402000137B> 
1 
#<EQL Hash Table{0} 4020001573> 
2 
3 
3   ; return value
CL-USER 62>(setf(gethash)(打印0)
(打印(下一个ht hts))
(打印件1)
(打印(1+)(打印(gethash 0
(打印(下一个ht hts))
2)))))
0
# 
1.
# 
2.
3.
3.返回值

为什么不宏扩展
setf
表单?这里是LispWorks:

CL-USER 32 > (pprint (macroexpand '(setf (gethash 0 (next-ht hts))
                                         (1+ (gethash 0 (next-ht hts))))))

(LET* ((#:|key1014| 0)
       (#:|table1015| (NEXT-HT HTS))
       (#:|default1016| NIL)
       (#:|store1017| (1+ (GETHASH 0 (NEXT-HT HTS)))))
  (SYSTEM::%PUTHASH #:|key1014| #:|table1015| #:|store1017|))
它有什么作用

  • 获取键值
  • 获取哈希表,调用
    NEXT-HT
  • 获取默认值,不使用
  • 获取新值,调用
    NEXT-HT
  • 使用特定于实现的方式将新的键/值存储到哈希表中
因此,显然
NEXT-HT
被调用了两次

它背后的粗略(!)概念模型是什么

  • setf将检查第一个表达式
  • 这是什么?哦,它是
    gethash
    ,让我为它设置setter表单
  • 然后,setter表单将从第一个表单计算必要的子表单
  • 然后它将计算新值
  • 将使用这些参数调用setter运算符
例如:

CL-USER 62 > (setf (gethash (print 0)
                            (print (next-ht hts))
                            (print 1))
                   (print (1+ (print (gethash 0
                                              (print (next-ht hts))
                                              2)))))

0 
#<EQL Hash Table{1} 402000137B> 
1 
#<EQL Hash Table{0} 4020001573> 
2 
3 
3   ; return value
CL-USER 62>(setf(gethash)(打印0)
(打印(下一个ht hts))
(打印件1)
(打印(1+)(打印(gethash 0
(打印(下一个ht hts))
2)))))
0
# 
1.
# 
2.
3.
3.返回值

SETF
不会阻止子窗体被计算两次。修改宏,比如
INCF
这样做。@jkiiski这就是我想要的区别。我以前的印象是,
setf
是一种修改宏,因为这个位置只是传递给
get setf expansion
。谢谢。
SETF
不会阻止子窗体被计算两次。修改宏,比如
INCF
这样做。@jkiiski这就是我想要的区别。我以前的印象是,
setf
是一种修改宏,因为这个位置只是传递给
get setf expansion
。谢谢。感谢您总结了
setf
评估模型。但是我试图更好地理解(参考上面的代码回顾文章)为什么acelent明确建议使用setf表达式而不是使用get setf expansion编写自己的修改宏。这是因为您自己必须小心地只对子窗体求值一次
get setf expansion
不会为您执行此操作。是这样吗?这可能有点“离题”,但我希望看到类似的
get setf expansion
操作摘要。我花了一段时间在上面的代码审查帖子中挖掘信息,现在我意识到我真的不需要问这个问题。大多数答案已经存在。请随意删除它,如果您有任何补充,也可以将其用于进一步的教育目的。但感谢您的时间、努力和澄清。感谢您总结了
setf
评估模型。但是我试图更好地理解(参考上面的代码回顾文章)为什么acelent明确建议使用setf表达式而不是使用get setf expansion编写自己的修改宏。这是因为您自己必须小心地只对子窗体求值一次
get setf expansion
不会为您执行此操作。是这样吗?这可能有点“离题”,但我希望看到类似的
get setf expansion
操作摘要。我花了一段时间在上面的代码审查帖子中挖掘信息,现在我意识到我真的不需要问这个问题