关于接口和回调函数的代码解释

huangapple go评论78阅读模式
英文:

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 缓存实现的代码片段。我有几个问题:

  1. entry 结构体有一个 Value 字段,我猜它是一个空接口。可能是 list.Element 中的节点值?我查看了 Go 中双向链表的实现。
type Element struct {
    Value interface{}
}

如果我的猜测正确,那么 entry 如何声明一个类型在另一个结构体(Element 结构体)中的字段?

  1. 当你尝试获取节点的 Value 时,为什么要使用类型断言 kv := ele.Value.(*entry),使用 kv := ele.Value 是否能达到相同的结果?

  2. OnEvicted() 函数是做什么的?函数可以在结构体中实现吗?请详细解释一下。

回答你的问题:

  1. 是的,你的猜测是正确的。entry 结构体中的 value 字段是一个空接口,用于存储 list.Element 中的节点值。在这个实现中,entry 结构体是为了将 key 和 value 组合在一起作为缓存的条目。

  2. 在这里,使用类型断言 kv := ele.Value.(*entry) 是为了将 ele.Value 转换为 *entry 类型,以便可以访问其中的字段。如果只使用 kv := ele.Value,那么 kv 的类型将是 interface{},无法直接访问 entry 结构体中的字段。

  3. OnEvicted() 函数是一个可选的回调函数,在条目被淘汰时执行。它接受两个参数:key 和 value。你可以在这个函数中实现自定义的逻辑,比如记录日志、持久化数据等。函数可以在结构体中实现,通过将函数作为字段保存在 Cache 结构体中,可以在需要时调用该函数。

英文:
package lru
import &quot;container/list&quot;
// 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 &amp;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(&amp;entry{key, value})
c.cache[key] = ele
c.nbytes += int64(len(key)) + int64(value.Len())
}
for c.maxBytes != 0 &amp;&amp; c.maxBytes &lt; 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:

  1. 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)

  1. when you try to get the Value of node, kv := ele.Value.(*entry) why use type assertion, does kv := ele.Value achieve the same result?

  2. what does OnEvicted() do? Can function be implemented within struct? Detailed explaination please.

答案1

得分: 1

  1. ...具有需要实现Value接口的值字段...(Value接口可以具有返回int的Len()方法)。

  2. kv := ele.Valuekv 声明为 interface{} 类型,kv := ele.Value.(*entry)kv 声明为 *entry 类型,否则在删除时无法使用 kv.key,因为 interface{} 不知道我有 key 字段。

  3. 如果提供了 OnEvicted,它将被调用并传入键和值(这可以是日志记录,或者可能是其他有用的东西)。

英文:
  1. ... has field of value that need to implement Value interface... (value interface can have Len() method that return int. )

  2. kv := ele.Value is making kv of type interface{}, kv := ele.Value.(*entry) is making kv of type *entry, otehrwise using kv.key in delete wouldn't be possible, as interface{} have no idea that I have key field.

  3. if OnEvicted provided, it will be called with key, and Value (this can be logging record, or maybe something else useful)

huangapple
  • 本文由 发表于 2021年8月4日 17:22:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/68648701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定