英文:
Code explanation about interface and callback function
问题
package lru
import "container/list"
// Cache 是一个 LRU 缓存。它不支持并发访问。
type Cache struct {
maxBytes int64
nbytes int64
ll *list.List
cache map[string]*list.Element
// 当一个条目被淘汰时,可选地执行的函数。
OnEvicted func(key string, value Value)
}
type entry struct {
key string
value Value
}
// Value 使用 Len 方法计算占用的字节数
type Value interface {
Len() int
}
// New 是 Cache 的构造函数
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
return &Cache{
maxBytes: maxBytes,
ll: list.New(),
cache: make(map[string]*list.Element),
OnEvicted: onEvicted,
}
}
// RemoveOldest 移除最旧的条目
func (c *Cache) RemoveOldest() {
ele := c.ll.Back()
if ele != nil {
c.ll.Remove(ele)
kv := ele.Value.(*entry)
delete(c.cache, kv.key)
c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
}
}
// Add 向缓存中添加一个值
func (c *Cache) Add(key string, value Value) {
if ele, ok := c.cache[key]; ok {
c.ll.MoveToFront(ele)
kv := ele.Value.(*entry)
c.nbytes += int64(value.Len()) - int64(kv.value.Len())
kv.value = value
} else {
ele := c.ll.PushFront(&entry{key, value})
c.cache[key] = ele
c.nbytes += int64(len(key)) + int64(value.Len())
}
for c.maxBytes != 0 && c.maxBytes < c.nbytes {
c.RemoveOldest()
}
}
我是一个 Golang 的新手。这是一个 LRU 缓存实现的代码片段。我有几个问题:
- entry 结构体有一个 Value 字段,我猜它是一个空接口。可能是 list.Element 中的节点值?我查看了 Go 中双向链表的实现。
type Element struct {
Value interface{}
}
如果我的猜测正确,那么 entry 如何声明一个类型在另一个结构体(Element 结构体)中的字段?
-
当你尝试获取节点的 Value 时,为什么要使用类型断言
kv := ele.Value.(*entry)
,使用kv := ele.Value
是否能达到相同的结果? -
OnEvicted() 函数是做什么的?函数可以在结构体中实现吗?请详细解释一下。
回答你的问题:
-
是的,你的猜测是正确的。entry 结构体中的 value 字段是一个空接口,用于存储 list.Element 中的节点值。在这个实现中,entry 结构体是为了将 key 和 value 组合在一起作为缓存的条目。
-
在这里,使用类型断言
kv := ele.Value.(*entry)
是为了将 ele.Value 转换为 *entry 类型,以便可以访问其中的字段。如果只使用kv := ele.Value
,那么 kv 的类型将是 interface{},无法直接访问 entry 结构体中的字段。 -
OnEvicted() 函数是一个可选的回调函数,在条目被淘汰时执行。它接受两个参数:key 和 value。你可以在这个函数中实现自定义的逻辑,比如记录日志、持久化数据等。函数可以在结构体中实现,通过将函数作为字段保存在 Cache 结构体中,可以在需要时调用该函数。
英文:
package lru
import "container/list"
// Cache is a LRU cache. It is not safe for concurrent access.
type Cache struct {
maxBytes int64
nbytes int64
ll *list.List
cache map[string]*list.Element
// optional and executed when an entry is purged.
OnEvicted func(key string, value Value)
}
type entry struct {
key string
value Value
}
// Value use Len to count how many bytes it takes
type Value interface {
Len() int
}
// New is the Constructor of Cache
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
return &Cache{
maxBytes: maxBytes,
ll: list.New(),
cache: make(map[string]*list.Element),
OnEvicted: onEvicted,
}
}
// RemoveOldest removes the oldest item
func (c *Cache) RemoveOldest() {
ele := c.ll.Back()
if ele != nil {
c.ll.Remove(ele)
kv := ele.Value.(*entry)
delete(c.cache, kv.key)
c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
}
}
// Add adds a value to the cache.
func (c *Cache) Add(key string, value Value) {
if ele, ok := c.cache[key]; ok {
c.ll.MoveToFront(ele)
kv := ele.Value.(*entry)
c.nbytes += int64(value.Len()) - int64(kv.value.Len())
kv.value = value
} else {
ele := c.ll.PushFront(&entry{key, value})
c.cache[key] = ele
c.nbytes += int64(len(key)) + int64(value.Len())
}
for c.maxBytes != 0 && c.maxBytes < c.nbytes {
c.RemoveOldest()
}
}
I am pretty new to golang. Here is a snippet of code of lru cache implementation.
I have a few questions:
- the entry struct has a field of Value, which I assume is an empty interface. Possibly a node value in list.Element? I check the implemtation of double linked list of go.
type Element struct {
Value interface{}
}
if the assumption is correct, how could entry declare a field which type is inside another struct (the Element struct)
-
when you try to get the Value of node,
kv := ele.Value.(*entry)
why use type assertion, doeskv := ele.Value
achieve the same result? -
what does OnEvicted() do? Can function be implemented within struct? Detailed explaination please.
答案1
得分: 1
-
...具有需要实现Value接口的值字段...(Value接口可以具有返回int的Len()方法)。
-
kv := ele.Value
将kv
声明为interface{}
类型,kv := ele.Value.(*entry)
将kv
声明为*entry
类型,否则在删除时无法使用kv.key
,因为 interface{} 不知道我有key
字段。 -
如果提供了
OnEvicted
,它将被调用并传入键和值(这可以是日志记录,或者可能是其他有用的东西)。
英文:
-
... has field of value that need to implement Value interface... (value interface can have Len() method that return int. )
-
kv := ele.Value
is makingkv
of typeinterface{}
,kv := ele.Value.(*entry)
is makingkv
of type*entry
, otehrwise usingkv.key
in delete wouldn't be possible, as interface{} have no idea that I havekey
field. -
if
OnEvicted
provided, it will be called with key, and Value (this can be logging record, or maybe something else useful)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论