我有一个相当简单的Go程序,它可以确定某人的事件时间表是否有重叠。本质上这就是它的作用:
我们有3个活动,比如说一家杂货店:
day 0 1 2 3 4 5
apple sale [-----------]
banana sale [--------]
pickle sale [-------------]
这是我的代码:
package main
import (
"fmt"
)
type event struct {
start int
end int
groups map[string]bool
}
func main(){
//Create Events
campaigns := []event{
event{
start: 0,
end: 4,
groups: map[string]bool{
"apple sale": true,
},
},
event{
start: 2,
end: 5,
groups: map[string]bool{
"banna sale": true,
},
},
event{
start: 3,
end: 10,
groups: map[string]bool{
"pickle sale": true,
},
},
}
fmt.Printf("n-------------n| Events |n-------------nn")
for _, c := range campaigns {
fmt.Printf("Name: ")
for name, _ := range c.groups {
fmt.Printf("%v, ", name)
}
fmt.Printf("nStart:%vnEnd:%vnn", c.start, c.end)
}
overlaps := recursiveOverlaps(campaigns, make([]event, 0))
fmt.Printf("n-------------n| Overlaps |n-------------nn")
for _, c := range overlaps {
fmt.Printf("Events: ")
for name, _ := range c.groups {
fmt.Printf("%v, ", name)
}
fmt.Printf("nStart:%vnEnd:%vnn", c.start, c.end)
}
}
func recursiveOverlaps(events []event, overlaps []event) []event {
//pop comparisonEvent (first item in array)
comparisonEvent, events := events[len(events)-1], events[:len(events)-1]
if len(events) == 0 {//check base case
return overlaps;
}
//Find overlaps
for _, eventItem := range events {
overlaping, overlapCase := overlapExists(comparisonEvent, eventItem)
if overlaping {
groups := mergeKeys(comparisonEvent.groups, eventItem.groups)
switch overlapCase {
case 1:
overlaps = append( overlaps, event{eventItem.start, eventItem.end, groups} )
case 2:
overlaps = append( overlaps, event{comparisonEvent.start, comparisonEvent.end, groups} )
case 3:
overlaps = append( overlaps, event{eventItem.start, comparisonEvent.end, groups} )
case 4:
overlaps = append( overlaps, event{comparisonEvent.start, eventItem.end, groups} )
}
//reset groups so we don't get any funny business
groups = map[string]bool{}
}
}
return recursiveOverlaps(events, overlaps)
}
func overlapExists(a event, b event) (bool, int) {
if between(a.start, a.end, b.start) && between(a.start, a.end, b.end) {
// [----------]
// [-----]
return true, 1
}
if between(b.start, b.end, a.start) && between(b.start, b.end, a.end) {
// [-----]
// [----------]
return true, 2
}
if between(a.start, a.end, b.start) {
// [----------]
// [--------------
return true, 3
}
if between(a.start, a.end, b.end) {
// [----------]
// --------------]
return true, 4
}
return false, 0
}
func between(a, b, c int) bool {
//is c between a and b?
if c > a && c < b {
return true
}
return false
}
// Given two maps merge right into left
func mergeKeys(left, right map[string]bool) map[string]bool {
for key, rightVal := range right {
left[key] = rightVal
}
return left
}
代码几乎工作。。。。但问题是,从逻辑上讲,我把它编程为只比较两个事件。不是一次三个。然而,结果是:
-------------
| Events |
-------------
Name: apple sale,
Start:0
End:4
Name: banna sale,
Start:2
End:5
Name: car sale,
Start:3
End:10
-------------
| Overlaps |
-------------
Events: banna sale, car sale, apple sale,
Start:3
End:4
Events: car sale, apple sale, banna sale,
Start:3
End:5
Events: banna sale, apple sale,
Start:2
End:4
不知怎么的,我觉得mergeKeys()
函数在做一些奇怪的事情。有什么想法吗?
好的。所以我发现了问题所在:
go中的地图是通过引用传递的。。。。有点不是
即使地图前面没有指针。。。它们是通过引用传递的。。。有点事实并非如此,但你可以在这篇文章中读到更多关于这一点的内容,";在go"中没有通过引用;以及这篇文章;如果映射不是引用变量,它是什么;。我将给你这段摘录,以澄清正在发生的事情:
map值是指向runtime.hmap结构的指针。
即使映射不是通过引用传递的,而是通过值传递的。。。传递到函数中的原始映射变量将被修改。这意味着,当我在mergeKeys函数中更改左侧映射时,我实际上也在更改comparisonEvent.groups映射。
为了解释这一点,我创建了一个垃圾图,我可以变异,不会产生任何后果:
if overlaping {
trashMap := make(map[string]bool)
for key, value := range comparisonEvent.groups {
trashMap[key] = value
}
groups := mergeKeys(trashMap, eventItem.groups)
switch overlapCase {
case 1:
overlaps = append( overlaps, event{eventItem.start, eventItem.end, groups} )
case 2:
overlaps = append( overlaps, event{comparisonEvent.start, comparisonEvent.end, groups} )
case 3:
overlaps = append( overlaps, event{eventItem.start, comparisonEvent.end, groups} )
case 4:
overlaps = append( overlaps, event{comparisonEvent.start, eventItem.end, groups} )
}
}
在我接受这个答案之前,我仍然愿意讨论和更好的解决方案。我希望这能帮助到外面的人!