The function makeEvenGenerator is supposed to return a function that generates even numbers in a sequential manner:
package main
import "fmt"
func makeEvenGenerator() func() uint {
i := uint(0)
return func() (ret uint) {
ret = i
i += 2
return
}
}func main() {
nextEven := makeEvenGenerator()
fmt.Println(nextEven()) // 0
fmt.Println(nextEven()) // 2
fmt.Println(nextEven()) // 4
}
When I run it, I get the error syntax error: unexpected func, expecting semicolon or newline and Non-declaration statement outside function body.
The code is taken verbatim from An Introduction to Programming in Go by Caleb Doxsey. I am not sure what the problem is.
You are missing a newline between the "}" at the end of the makeEvenGenerator and the "func" in main.
I fixed the error and posted the code to the playground.
There are rules about semicolons.
The Go Programming Language Specification
Semicolons
The formal grammar uses semicolons ";" as terminators in a number of
productions. Go programs may omit most of these semicolons using the
following two rules:
When the input is broken into tokens, a semicolon is automatically inserted into the token stream at the end of a non-blank line if the
line's final token is
an identifier
an integer, floating-point, imaginary, rune, or string literal
one of the keywords break, continue, fallthrough, or return
one of the operators and delimiters ++, --, ), ], or }
To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".
The error is here,
}func main() {
Write,
}
func main() {
You were missing a new line between } at the end of makeEvenGenerator and func main.
Also an alternative approach for that pattern is to use a channel not return a function:
func evenGenerator() <-chan uint {
ch := make(chan uint)
go func() {
i := uint(0)
for {
ch <- i
i += 2
}
}()
return ch
}
func main() {
evens := evenGenerator()
for i := 0; i < 3; i++ {
fmt.Println(<-evens)
}
}
playground
Related
I want to create a function which does the same as json.Unmarshal, but before that it needs to remove all JSON encoded unicode control characters which are not spaces.
func UnmarshalJSON(data []byte, v interface{}) error {
// TODO: implement the removeControlCharacters function
// cleanData, err := removeControlCharacters(data)
// if err != nil { return err }
return json.Unmarshal(cleanData, v)
}
I already got that same removal function working for characters which are not JSON encoded like so:
cleanData := strings.Map(func(r rune) rune {
if unicode.IsControl(r) && !unicode.IsPrint(r) && !unicode.IsSpace(r) {
return -1
}
return r
}, string(data))
However, in JSON these characters are encoded like \u0000. Also there's the problem of having escaped slashes. So when the JSON data (as a string) looks like this:
{"name":"\b\t\u0009Some\u0000thing\\u0002"}
I expect an output like this (also as a string):
{"name":"\t\u0009Something\\u0002"}
Is there a way to remove these characters in a relatively clean way? Is there some preexisting code somewhere which I could use? It's a bit annoying to create very low level code for something quite basic (in my opinion)
We are trying to parse a csv file using Go's encoding/csv package. This particular csv is a bit peculiar, each row has a trailing space. When trying to decode this csv with quoted fields the package breaks since it expects a newline, separator or quote. The trailing space is not expected.
How would you handle this case? Do you know of another parser that we could use?
Edit:
f,err := os.Open("file.go")
// err etc..
csvr := csv.NewReader(f)
csvr.Comma = csvDelimiter
for {
rowAsSlice, err := csvr.Read()
// Handle row and errors etc.
}
Edit 2:
CSV example, mind the trailing space!
"RECORD_TYPE","COMPANY_SHORTNAME"
"HDR","COMPANY_EXAMPLE"
One possible solution is to wrap the source file reader in a custom reader whose Read(...) method silently trims trailing whitespace from what the underlying reader actually reads. The csv.Reader could use that type directly.
For example (Go Playground):
type TrimReader struct{ io.Reader }
var trailingws = regexp.MustCompile(` +\r?\n`)
func (tr TrimReader) Read(bs []byte) (int, error) {
// Perform the requested read on the given reader.
n, err := tr.Reader.Read(bs)
if err != nil {
return n, err
}
// Remove trailing whitespace from each line.
lines := string(bs[:n])
trimmed := []byte(trailingws.ReplaceAllString(lines, "\n"))
copy(bs, trimmed)
return len(trimmed), nil
}
func main() {
file, err := file.Open("myfile.csv")
// TODO: handle err...
csvr := csv.NewReader(TrimReader{file})
for {
record, err := csvr.Read()
if err == io.EOF {
break
}
fmt.Printf("LINE: record=%#v, err=%v\n", record, err)
}
// LINE: record=[]string{"RECORD_TYPE", "COMPANY_SHORTNAME"}, err=<nil>
// LINE: record=[]string{"HDR", "COMPANY_EXAMPLE"}, err=<nil>
}
Note that, as commenter #svsd points out, there is a subtle bug here wherein trailing whitespace can still make it through if the line terminator isn't read until the subsequent call. You can workaround by buffering or, perhaps best, simply preprocess these CSV files to remove the trailing whitespace before attempting to parse them.
I am reading in a file via the command line.
As the file is a JSON export from Oracle, it has a certain structure. This default structure is not valid JSON for some reason. Example:
// This isn't valid JSON
,"items":
[
{"id":123,"language":"ja-JP","location":"Osaka"}
,{"id":33,"language":"ja-JP","location":"Tokyo"}
,{"id":22,"language":"ja-JP","location":"Kentok"}
]}
I wish for it to only be an array of object, thus having the expected output:
// This is valid json
[
{"id":123,"language":"ja-JP","location":"Osaka"}
,{"id":33,"language":"ja-JP","location":"Tokyo"}
,{"id":22,"language":"ja-JP","location":"Kentok"}
]
Therefore, I need to remove line 1(entirely) as well as the last } from the last line of the file.
The file is being parsed via commandline from the input:
file, err := ioutil.ReadFile(os.Args[1])
I am trying to remove the invalid strings/words this way, but it does not reformat anything:
// in func main()
removeInvalidJSON(file, os.Args[1])
// later on ..
func removeInvalidJSON(file []byte, path string) {
info, _ := os.Stat(path)
mode := info.Mode()
array := strings.Split(string(file), "\n")
fmt.Println(array)
//If we have the clunky items array which is invalid JSON, remove the first line
if strings.Contains(array[0], "items") {
fmt.Println("Removing items")
array = append(array[:1], array[1+1:]...)
}
// Finds the last index of the array
lastIndex := array[len(array)-1]
// If we have the "}" in the last line, remove it as this is invalid JSON
if strings.Contains(lastIndex, "}") {
fmt.Println("Removing }")
strings.Trim(lastIndex, "}")
}
// Nothing changed?
fmt.Println(array)
ioutil.WriteFile(path, []byte(strings.Join(array, "\n")), mode)
}
The above function does write to the file I can see - but it does not alter the array as far as I can tell, and does not write it into the file.
How do I effectively remote the first line of the file, as well as the last false curly brace } from the file?
I unmarshall the JSON in another function: Is there a method of doing it more "cleanly" using the "encoding/json" library?
There are several significant issues with this code causing it to behave not as intended. I've noted these with comments below:
func removeInvalidJSON(file []byte, path string) {
info, _ := os.Stat(path)
mode := info.Mode()
array := strings.Split(string(file), "\n")
fmt.Println(array)
//If we have the clunky items array which is invalid JSON, remove the first line
if strings.Contains(array[0], "items") {
fmt.Println("Removing items")
// If you just want to remove the first item, this should be array = array[1:].
// As written, this appends the rest of the array to the first item, i.e. nothing.
array = append(array[:1], array[1+1:]...)
}
// Finds the last ~index~ *line* of the array
lastIndex := array[len(array)-1]
// If we have the "}" in the last line, remove it as this is invalid JSON
if strings.Contains(lastIndex, "}") {
fmt.Println("Removing }")
// Strings are immutable. `strings.Trim` does nothing if you discard the return value
strings.Trim(lastIndex, "}")
// After the trim, if you want this to have any effect, you need to put it back in `array`.
}
// Nothing changed?
fmt.Println(array)
ioutil.WriteFile(path, []byte(strings.Join(array, "\n")), mode)
}
I think what you want is something more like:
func removeInvalidJSON(file []byte, path string) {
info, _ := os.Stat(path)
mode := info.Mode()
array := strings.Split(string(file), "\n")
fmt.Println(array)
//If we have the clunky items array which is invalid JSON, remove the first line
if strings.Contains(array[0], "items") {
fmt.Println("Removing items")
array = array[1:]
}
// Finds the last line of the array
lastLine := array[len(array)-1]
array[len(array)-1] = strings.Trim(lastLine, "}")
fmt.Println(array)
ioutil.WriteFile(path, []byte(strings.Join(array, "\n")), mode)
}
Reading the source code of math/floor.go, starting from line 13, I read some code like this:
func Floor(x float64) float64
func floor(x float64) float64 {
if x == 0 || IsNaN(x) || IsInf(x, 0) {
return x
}
if x < 0 {
d, fract := Modf(-x)
if fract != 0.0 {
d = d + 1
}
return -d
}
d, _ := Modf(x)
return d
}
It seems the func Floor has no body. I tried to copy and paste these code in my go file. it doesn't compile. The error message is missing function body. So my question is: is a bodiless function legal in Go's syntax? Thanks.
It's the way how functions are implemented in assembly. You find the assembly implementation in the floor_ARCH.s (e.g.: AMD64) files.
To quote the spec:
A function declaration may omit the body. Such a declaration provides the signature for a function implemented outside Go, such as an assembly routine.
In my case I had "../../../pkg/mod/golang.org/x/tools#v0.0.0-20190814235402-ea4142463bf3/go/ssa/interp/testdata/src/fmt/fmt.go:3:6: missing function body" error!
and it was because I used fmt without importing it so my IDE imported the wrong package.
I fixed it by removing the import (golang.org/x/tools/go/ssa/interp/testdata/src/fmt)
and just importing fmt
I have to write unit tests for several functions with similar signature and return values (an object and an error), which must pass similar test conditions.
I would like to avoid writing:
func TestFunc1(t *testing.T) {
// tests on return values
}
func TestFunc2(t *testing.T) {
// tests identical for Func1
}
func TestFunc3(t *testing.T) {
// tests identical for Func1
}
...
(See this go playground example for a more complete context)
(yes, go playground doesn't support yet go test, only go run, and issue 6511 is there to request that feature)
How would you use reflection (reflect package) in order to write only one test which would:
call each function in turn?
test their return value?
I have seen:
"How to properly use .Call in reflect package, Golang?", using Value.Call
"Selecting a function from a list of functions in Golang"
But I miss a complete example for calling functions and using the returned values in a test.
Once I understood that everything must use or return the type Value, here is what I came up with.
The trick is to use:
ValueOf in order to get a value of the receiver
Value.MethodByName to find a function of that receiver value
Value.IsNil to test for nil returned value.
Main extract of the test code:
var funcNames = []string{"Func1", "Func2", "Func3"}
func TestFunc(t *testing.T) {
stype := reflect.ValueOf(s)
for _, fname := range funcNames {
fmt.Println(fname)
sfunc := stype.MethodByName(fname)
// no parameter => empty slice of Value
ret := sfunc.Call([]reflect.Value{})
val := ret[0].Int()
// That would panic for a nil returned err
// err := ret[1].Interface().(error)
err := ret[1]
if val < 1 {
t.Error(fname + " should return positive value")
}
if err.IsNil() == false {
t.Error(fname + " shouldn't err")
}
}
}
See a runnable example in go playground.
Note that if you are calling that test function with a non-existent function name, that will panic.
See that example here.
runtime.panic(0x126660, 0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:266 +0xe0
testing.funcĀ·005()
/tmp/sandbox/go/src/pkg/testing/testing.go:383 +0x180
----- stack segment boundary -----
runtime.panic(0x126660, 0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:248 +0x160
reflect.flag.mustBe(0x0, 0x13)
/tmp/sandbox/go/src/pkg/reflect/value.go:249 +0xc0
reflect.Value.Call(0x0, 0x0, 0x0, 0xfeef9f28, 0x0, ...)
/tmp/sandbox/go/src/pkg/reflect/value.go:351 +0x40
main.TestFunc(0x10546120, 0xe)
/tmpfs/gosandbox-3642d986_9569fcc1_f443bbfb_73e4528d_c874f1af/prog.go:34 +0x240
Go playground recover from that panic, but your test program might not.
That is why I added to the test function above:
for _, fname := range funcNames {
defer func() {
if x := recover(); x != nil {
t.Error("TestFunc paniced for", fname, ": ", x)
}
}()
fmt.Println(fname)
That produces (see example) a much nicer output:
Func1
Func2
Func3
Func4
--- FAIL: TestFunc (0.00 seconds)
prog.go:48: Func2 should return positive value
prog.go:51: Func3 shouldn't err
prog.go:32: TestFunc paniced for Func4 : reflect: call of reflect.Value.Call on zero Value
FAIL