针对不同包的Golang函数泛化

时间:2018-02-17 04:10:04

标签: go function-pointers generic-programming

想象一下,需要使用这些函数如何使这些调用通用,这样我就不会重复几乎相同的代码。

with“encoding / csv”

func getDataFromCSVFiles(files []string) (error, Data) {
        data := Data{}
        for _, file := range files {
            f, err := os.Open(file)
            if err != nil {
                panic(err)
                return err, data
            }
            defer f.Close()
            r := charmap.ISO8859_1.NewDecoder().Reader(f)
            reader := csv.NewReader(r)
            for i := 1;;i++ {
                rec, err := reader.Read()
                if i == 1 {
                    //Skipping header
                    continue
                }
                if err != nil {
                    if err == io.EOF {
                        break
                    }
                    //TODO log error line and csv filename
                    log.Fatal(err)
                }
                addWorkbook(rec, &data)
            }
        }
        return nil, data
    }

和     import fw“github.com/hduplooy/gofixedwidth”,除了调用fw.NewReader

之外几乎一样
func getDataFromPRNFiles(files []string) (error, Data) {
    data := Data{}
    for _, file := range files {
        f, err := os.Open(file)
        if err != nil {
            panic(err)
            return err, data
        }
        defer f.Close()
        r := charmap.ISO8859_1.NewDecoder().Reader(f)
        reader := fw.NewReader(r)
        for i := 1;;i++ {
            rec, err := reader.Read()
            if i == 1 {
                //Skipping header
                continue
            }
            if err != nil {
                if err == io.EOF {
                    break
                }
                //TODO log error line and csv filename
                log.Fatal(err)
            }
            addWorkbook(rec, &data)
        }
    }
    return nil, data
}

1 个答案:

答案 0 :(得分:2)

唯一明显的区别是:

reader := csv.NewReader(r)

reader := fw.NewReader(r)

我不确定fw是什么,但可能两个读者都实现了一个共同的界面:

type StringSliceReader interface {
    Read() ([]string, error)
}

所以你可以将开启者(csv.NewReaderfw.NewReader)作为函数参数传递:

func getDataFromFiles(files []string, func(r io.Reader) StringArrayReader) (error, Data) {
    //...
}

但是您需要将它们包装在小函数中以绕过返回类型:

func newCSVReader(r io.Reader) StringSliceReader {
    return csv.NewReader(r)
}

func newFWReader(r io.Reader) StringSliceReader {
    return fw.NewReader(r)
}

此外,当{em>函数退出时,defer将要执行的事项排队,而不是在循环的下一次迭代中。所以,如果你这样做:

for _, file := range files {
    f, err := os.Open(file)
    if err != nil {
        panic(err)
        return err, data
    }
    defer f.Close()
    //...
}

files有一百个条目,然后在关闭其中任何一个之前你将有一百个打开的文件。您可能希望将该循环体移动到单独的函数中,这样您一次只能打开一个文件。

此外,error通常是函数的最后一个返回值,因此您应return data, err更加惯用。

结果可能如下所示:

type StringSliceReader interface {
    Read() ([]string, error)
}

type NewReader func(r io.Reader) StringSliceReader

func newCSVReader(r io.Reader) StringSliceReader {
    return csv.NewReader(r)
}

func newFWReader(r io.Reader) StringSliceReader {
    return fw.NewReader(r)
}

func getDataFrom(file string, data *Data, newReader NewReader) error {
    f, err := os.Open(file)
    if err != nil {
        return err
    }
    defer f.Close()

    r := charmap.ISO8859_1.NewDecoder().Reader(f)
    reader := newReader(r)
    for i := 1; ; i++ {
        rec, err := reader.Read()
        if i == 1 {
            continue
        }
        if err != nil {
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }
        addWorkbook(rec, data)
    }
    return nil
}

func getDataFromFiles(files []string, newReader NewReader) (Data, error) {
    data := Data{}
    for _, file := range files {
        err := getDataFrom(file, newReader, &data)
        if err != nil {
            panic(err)
            return data, err
        }
    }
    return data, nil
}

你可以说getDataFromFiles(files, newCSVReader)读取CSV或getDataFromFiles(files, newFWReader)来读取FW文件。如果你想从别的东西中读取,你只需要一个NewReader函数和一些实现StringSliceReader接口的东西。

您可能希望在charmap.ISO8859_1.NewDecoder().Reader(f)函数中隐藏/隐藏NewReader内容,以便更轻松地读取非Latin-1编码文件。您也可以使用newReader NewReader中的map[string]NewReader替换getDataFromFiles,并根据文件的扩展名或其他格式标识符选择要使用的NewReader