2026/08/10

02:16 Go is a terrible CP language

Last time I did competitive programming was a while ago, I used to do that in C++. I have an important call soon which will probably include coding, and I'm getting prepared. Usually I would do that in Lisp, because I feel most proficient at. Sadly Lisp is not very readable by people who are not experienced with it, that's why I have to pick a language that its syntax is more common (c-family like). For me that's Go (since I have almost forgot how to deal with most of C++ quirks). I learnt (perhaps too late) that Go is not a good pick for CP.

This also comes with a realization that Go's philosophy of Clear better than clever comes with many tradeoffs when writing code. Some examples from top of my mind;

Heap in C++:

#include 

priority_queue pq;

pq.push(5);
pq.push(2);
pq.push(10);

pq.top(); // 10
pq.pop();

The equivalent in Go requires implementing heap.Interface yourself:

Heap in Go:

import "container/heap"

type IntHeap []int

func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x any) {
*h = append(*h, x.(int))
}

func (h *IntHeap) Pop() any {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}

func main() {
h := &IntHeap{}
heap.Init(h)

heap.Push(h, 5)
heap.Push(h, 2)
heap.Push(h, 10)

x := (*h)[0] // 2
heap.Pop(h)
}

And if you want a max-heap, you also have to change Less:

func (h IntHeap) Less(i, j int) bool {
return h[i] > h[j]
}

Compare that with:

priority_queue pq;

Sets in C++

C++ has a proper ordered set:

#include 

set s;

s.insert(5);
s.insert(10);
s.insert(3);

s.erase(5);

if (s.count(10)) {
// exists
}

A C++ set is also ordered, so you get operations such as lower_bound:

auto it = s.lower_bound(7);

Go doesn't have a built-in set type.

The common solution is to use a map:

s := map[int]bool{}

s[5] = true
s[10] = true
s[3] = true

delete(s, 5)

if s[10] {
// exists
}

You can also use map[int]struct{}, which is the more idiomatic representation when the value itself doesn't matter:

s := map[int]struct{}{}

s[5] = struct{}{}
s[10] = struct{}{}

delete(s, 5)

if _, ok := s[10]; ok {
// exists
}

This works well for a hash set, but it is not equivalent to C++'s ordered set.

If you need the elements sorted in Go, you have to do it yourself:

keys := make([]int, 0, len(s))

for x := range s {
keys = append(keys, x)
}

sort.Ints(keys)

Whereas in C++ the ordering is part of the data structure itself.

Multiset in C++

C++ also has multiset, which allows duplicate values while keeping them ordered:

multiset ms;

ms.insert(5);
ms.insert(5);
ms.insert(10);
ms.insert(3);

ms.erase(ms.find(5)); // erase one occurrence

if (ms.count(5)) {
// exists
}

You can also get the number of occurrences directly:

ms.count(5);

Lower bound / upper bound

C++:

vector v = {1, 3, 3, 5, 7, 10};

auto it = lower_bound(v.begin(), v.end(), 5);

int index = it - v.begin();

Go:

v := []int{1, 3, 3, 5, 7, 10}

index := sort.Search(len(v), func(i int) bool {
return v[i] >= 5
})

C++'s STL was designed around of generic algorithmic programming.

Go's "clear is better than clever" philosophy can sometimes result in writing more for relatively smaller algorithmic ideas.

Take for example:

"Put everything in a min-heap and repeatedly take the smallest element."

In C++:

priority_queue, greater> pq;

In go that has to go through some boilerplate first.

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论