patterngoMinor
A* Go application
Viewed 0 times
applicationstackoverflowprogramming
Problem
I've written a generic implementation of the A* algorithm as a first Go
program, since I had implemented that both in C and in Python before and Go
reminds me a bit of both. I'm looking for general commentary on bad practices,
specially concerning the following:
References
I don't think I've ever seen a struct being passed by value in C, though I can
think of some use cases. In Python, class instances are also "passed by
reference". Is that the way to go with Go? The lack of
tempting to pass something like
like
and I guess the copy overhead would add up.
Slices
According to the docs I should think of them as "a pointer to a struct
containing a pointer to an array and a length", and the array has its length
embedded in the type. So should I even be using slices in here? Shouldn't I just
use an array, growing it as I go?
Ranges
I'm using them like a
in each iteration. Then again, with a large number of iterations isn't that a
bit costly? Perhaps I should be using a C-like
General advice is also appreciated. I understand the code is a bit lengthy, I'm
not asking for any in-depth analysis though, just an overview.
```
package main
import "fmt"
// TODO separate things in packages
/ Error codes /
const (
SUCCESS int = iota
INTERNAL_ERROR
NO_PATH
TOO_FAR
UNAVAILABLE
)
/ Define any object here /
type Obj struct {
attr int8
}
/*
Define the positional/distances type to be used in geo funcitons. This can be
any numerical type, signed or unsigned, integer or real.
*/
type Coord int64;
/ Define any N-dimentional position struct here /
type Pos struct {
y Coord
x Coord
}
/ A grid represented by a hash table of the form { position : object } /
type Grid map[Pos]Obj
/ Don't A if dst further than this, and stop iterat
program, since I had implemented that both in C and in Python before and Go
reminds me a bit of both. I'm looking for general commentary on bad practices,
specially concerning the following:
References
I don't think I've ever seen a struct being passed by value in C, though I can
think of some use cases. In Python, class instances are also "passed by
reference". Is that the way to go with Go? The lack of
const makes ittempting to pass something like
Pos by value, though I guess in a functionlike
astar that might not be a good idea, as there can be a lot of iterationsand I guess the copy overhead would add up.
Slices
According to the docs I should think of them as "a pointer to a struct
containing a pointer to an array and a length", and the array has its length
embedded in the type. So should I even be using slices in here? Shouldn't I just
use an array, growing it as I go?
Ranges
I'm using them like a
for i in x in Python, though I read a copy is createdin each iteration. Then again, with a large number of iterations isn't that a
bit costly? Perhaps I should be using a C-like
for i = 0; ... loop instead?General advice is also appreciated. I understand the code is a bit lengthy, I'm
not asking for any in-depth analysis though, just an overview.
```
package main
import "fmt"
// TODO separate things in packages
/ Error codes /
const (
SUCCESS int = iota
INTERNAL_ERROR
NO_PATH
TOO_FAR
UNAVAILABLE
)
/ Define any object here /
type Obj struct {
attr int8
}
/*
Define the positional/distances type to be used in geo funcitons. This can be
any numerical type, signed or unsigned, integer or real.
*/
type Coord int64;
/ Define any N-dimentional position struct here /
type Pos struct {
y Coord
x Coord
}
/ A grid represented by a hash table of the form { position : object } /
type Grid map[Pos]Obj
/ Don't A if dst further than this, and stop iterat
Solution
Bug
Your code clearly states:
But, when I run your code, I get:
Everything in reverse order. It is clear that your back-track to reverse the path taken is appending each point to the
Having said that, the append is probably the right solution, but a slice reversal afterwards would be better.
Your current back-track function is:
I would instead make that:
Notice how I put the loop conditions in to the for-loop, which makes your out-of-loop
Then, I use the simple swap mechanism in go to reverse the
Code Style
You have a number of convention-breaking habits in your code. For example, the following:
Function names in go should not have any underscores in them, and should be
The underscore
That declaration should be:
When running your code through the go lint-checker, it reported a number of problems in addition to the above (though the above code is what prompted me to run the lint checker):
Running the
Naming
One item that had me confused for a bit, is this code:
You define a "coordinate" as a single int64 - but, a coordinate is at least a 2 dimensional thing..... right? What you have there, is a distance. You call it a distance in some places, but in other places it makes the code funny... for example, here we have a function called
You have been overly enthusiastic in your type declaration for
Your code clearly states:
Src: 0, 0
Dst: 0, 5
Excpeted answer: {(0,0), (1,1), (2,2), (2,3), (2,4), (1,5), (0,5)}But, when I run your code, I get:
[{0 5} {1 5} {2 4} {2 3} {2 2} {1 1} {0 0}]Everything in reverse order. It is clear that your back-track to reverse the path taken is appending each point to the
ans slice, instead of inserting each point at the beginning - and getting things in the right order.Having said that, the append is probably the right solution, but a slice reversal afterwards would be better.
Your current back-track function is:
/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {
ans := []Pos{cur}
in := true
for in {
cur = came_from[cur]
ans = append(ans, cur)
_, in = came_from[cur]
}
return ans
}I would instead make that:
/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {
ans := []Pos{cur}
for cur, ok := came_from[cur]; ok; cur, ok = came_from[cur] {
ans = append(ans, cur)
}
sz := len(ans)
for i := sz / 2; i >= 0; i-- {
ans[i], ans[sz - i - 1] = ans[sz - i - 1], ans[i]
}
return ans
}Notice how I put the loop conditions in to the for-loop, which makes your out-of-loop
in variable now in-scope for the loop. Also, I renamed it to ok to conform to common practice in go.Then, I use the simple swap mechanism in go to reverse the
ans slice.Code Style
You have a number of convention-breaking habits in your code. For example, the following:
/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {Function names in go should not have any underscores in them, and should be
MixedCase (for exported functions) or mixedCase for internal functions. This is true for variable names as well.The underscore
_ or blank identifier has a special meaning in Go, and should be avoided in names.That declaration should be:
func reconstructPath(cameFrom map[Pos]Pos, cur Pos) []Pos {When running your code through the go lint-checker, it reported a number of problems in addition to the above (though the above code is what prompted me to run the lint checker):
astar.go:10:3: don't use ALL_CAPS in Go names; use CamelCase
astar.go:11:3: don't use ALL_CAPS in Go names; use CamelCase
astar.go:12:3: don't use ALL_CAPS in Go names; use CamelCase
astar.go:16:1: comment on exported type Obj should be of the form "Obj ..." (with optional leading article)
astar.go:21:1: comment on exported type Coord should be of the form "Coord ..." (with optional leading article)
astar.go:27:1: comment on exported type Pos should be of the form "Pos ..." (with optional leading article)
astar.go:33:1: comment on exported type Grid should be of the form "Grid ..." (with optional leading article)
astar.go:36:1: comment on exported const MAX_PATHFINDING_STEPS should be of the form "MAX_PATHFINDING_STEPS ..."
astar.go:37:7: don't use ALL_CAPS in Go names; use CamelCase
astar.go:60:3: don't use underscores in Go names; var to_be_visited should be toBeVisited
astar.go:61:3: don't use underscores in Go names; var came_from should be cameFrom
astar.go:62:3: don't use underscores in Go names; var d_src should be dSrc
astar.go:63:3: don't use underscores in Go names; var d_dst should be dDst
astar.go:77:9: don't use underscores in Go names; var temp_gscore should be tempGscore
astar.go:89:10: if block ends with a return statement, so drop this else and outdent its block
astar.go:97:11: should omit type Coord from declaration of var min; it will be inferred from the right-hand side
astar.go:108:6: don't use underscores in Go names; func _reconstruct_path should be _reconstructPath
astar.go:108:24: don't use underscores in Go names; func parameter came_from should be cameFromRunning the
code fmt on your code also made a difference - mostly in the spaces/tabs situation - go uses tabs.... for better or worse, always.Naming
One item that had me confused for a bit, is this code:
type Coord int64;You define a "coordinate" as a single int64 - but, a coordinate is at least a 2 dimensional thing..... right? What you have there, is a distance. You call it a distance in some places, but in other places it makes the code funny... for example, here we have a function called
mdistance which returns a Coord... ??? ...:/* Manhattan distance */
func mdistance(a, b *Pos) Coord {
return abs(a.y - b.y) + abs(a.x - b.x)
}You have been overly enthusiastic in your type declaration for
Coord, I think, and it should just be removed. Also, there's no real reason to make it an int64. A simple int will do fine - and you're probably on a 64-bit platform anyway.Code Snippets
Src: 0, 0
Dst: 0, 5
Excpeted answer: {(0,0), (1,1), (2,2), (2,3), (2,4), (1,5), (0,5)}[{0 5} {1 5} {2 4} {2 3} {2 2} {1 1} {0 0}]/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {
ans := []Pos{cur}
in := true
for in {
cur = came_from[cur]
ans = append(ans, cur)
_, in = came_from[cur]
}
return ans
}/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {
ans := []Pos{cur}
for cur, ok := came_from[cur]; ok; cur, ok = came_from[cur] {
ans = append(ans, cur)
}
sz := len(ans)
for i := sz / 2; i >= 0; i-- {
ans[i], ans[sz - i - 1] = ans[sz - i - 1], ans[i]
}
return ans
}/* For internal use */
func _reconstruct_path(came_from map[Pos]Pos, cur Pos) []Pos {Context
StackExchange Code Review Q#114957, answer score: 9
Revisions (0)
No revisions yet.