Golang命名规范:如何区分N和n

Golang命名规范:如何区分N和n 你好,

我正在编写一个用于分层抽样相关调查的包。在所有相关公式中,使用了以下统计特征:

  • N 表示总体大小
  • n 表示从总体中抽取的样本大小
  • Nh 表示第 h 层的规模
  • nh 表示从第 h 层抽取的样本大小

我曾用其他几种语言编写过类似的程序,并且总是使用相应的命名约定来表示上述特征:

  • N 对应 N(一个整数)
  • n 对应 n(一个整数)
  • N_h 对应 Nh(一个切片)
  • n_h 对应 nh(一个切片)

对于了解分层抽样(算法核心)的人来说,这就像太阳一样清晰。现在,在 Go 中该如何处理呢?我不能使用小写的 n,但我必须区分 nN。我可以使用 PopulationNSampleN,尽管这有点冗长。但是如何区分切片 N_hn_h 呢?我不应该使用下划线,所以我可能会用 PopulationNhSampleNh,尽管两者都以大写 N 开头,而所有公式都明确区分了 N(表示总体大小)和 n(表示样本大小)。

我从事这方面工作已经超过15年了,我认为使用以下四个变量名应该没问题:PopulationNSampleNPopulationNhSampleNh,但它们读起来不自然,这是我第一次遇到语言的命名约定让变量名听起来不自然的情况。

也许用 PopNhSampNh?读起来也不好。那么用 PNhSnh 呢?不行,这也不行:P 代表比例,S 代表标准差(我指的是相应的公式,而不仅仅是统计学的一般概念)。因此,我认为我应该使用更长的名字,尽管最短的版本有一个优点:我区分了大写和小写的“n”,这代表了统计公式中的实际名称。但我不认为这在 PopulationNhSamplenh 中会奏效,因为它们读起来不好。除非我用 Population_NhSample_nh……?但这就像重复传递相同的信息,因此前缀(Population_Sample_)是冗余的,它们的存在仅仅是因为我不能以一个小写字母开始变量名。

有没有什么情况下我可以打破 Go 的命名约定,比如不使用下划线?或者你有一些想法,可以让这些特定的名字更好?我理解这是一个特殊情况,因为我需要在 Go 中找到公式的表示方法,其中 nN 不同,而我在 Go 中无法直接做到这一点。

当然,我可以使用各种名字,如上所述,但我总是非常注意良好的命名,既要能很好地代表现象(这里是统计公式),同时又要读起来顺畅。

嗯,最后一个想法:也许 _Nh_nh 可以?


更多关于Golang命名规范:如何区分N和n的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

不,遗憾的是,那个小写的 nh 字段并未导出。

更多关于Golang命名规范:如何区分N和n的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


它们不是常量,而是切片。它们都将构成结构体的字段,但两者都需要被导出,因此都需要以大写字母开头。

简短的回答是你不能。我试图为你寻找替代方案。在任何情况下,你都不能拥有已导出的、小写字母开头的字段。

请原谅,我对“与分层相关的抽样内容”一点也不熟悉,所以对此有点不清楚。使用 Nhnh 有什么问题吗?这些是包级常量吗?

哎呀。但这正是我提出问题的来源和背景:我需要同时导出 Nhnh,这就导致了所有的问题。否则我直接使用它们就行了,但我确实需要同时导出这两个变量。

谢谢。我需要好好考虑一下,或者更确切地说,尝试一下这个方法是否如预期般有效。但目前看来,这似乎是个相当不错的想法。谢谢!我会尝试实践一下,稍后再回来分享我的经验。

难道你不能将你的 Sample.nh 字段直接命名为 Sample.Nh 吗?我知道这可能会造成混淆,但由于它是名为“Sample”的结构体上的一个字段,这是否足以表明你指的是样本的 nh 而非总体的 Nh

我明白你之前说过了;抱歉。现在懂了!

你是否需要修改这些切片?那么对于返回多个值的函数呢?

type S struct {
    vars struct {
        Nh []int
        nh []int
    }
}

func (s *S) Nhnh() (Nh, nh []int) {
    return s.vars.Nh, s.vars.nh
}

嗯,这就是生活 😉。所以,显然我必须想出点什么。那些冗长的名字并不好,所以我可能需要想出一个更好的解决方案。如果失败了,我就只能使用它们了。

我还有一个想法,也许不完美,但或许不错。我在考虑无论如何都使用 nh 和相关字段(以小写字母开头),因为它们会在计算中被大量使用,并且应该能很好地代表统计公式中的对应部分——但不需要导出它们,因为所有计算都是在内部完成的。一旦推导出最终解决方案,我会将这些小写字段(如 nh)的最优值赋值给另一个结构体(例如 FinalSolution),该结构体将包含最终的分层结果。在这个结构体中,我会使用冗长的字段名,因为不再需要让它反映任何公式。我并不是说这是理想的解决方案,但它会使用能很好代表相应统计公式的名称。

我认为这个问题很好地表明,人们确实需要花很多心思在代码设计上。也许这对你来说是家常便饭,但我习惯于使用 Python 和 R,它们的命名似乎更简单。但这种简单性常常会导致相当糟糕的名字,如果一个人不太注意命名,只是想到什么就用什么的话。Go 的命名约定不鼓励使用长名字(而我本人喜欢描述性的名字),因此需要仔细斟酌使用哪些名字,这是一件好事,可以极大地提升代码设计。

nh
FinalSolution

再次感谢你,Sean。以下是我的做法(初步方案,后续效果有待观察):

type Stratification struct {
	Stratum    []int     // 层分配(长度为N)
	Nh         []int     // 各层规模
	Wh         []int     // 各层权重
	Sh         []float64 // 各层标准差
	OptFun     float64   // 优化函数值
	Conditions bool      // 分层是否满足所有条件?
	Population           // 总体表示
	Sample               // 给定分层下的样本表示
}

type Population struct {
	X    []float64 // 辅助变量
	N    int     // 总体规模
	L    int     // 层数
	Mean float64 // X的总体均值
}

type Sample struct {
	n  int     // 预设总样本量
	cv float64 // 预设变异系数
	nh []int   // 各层样本量
}

(我使用了三个结构体,因为它们分别对应待解决问题的不同部分。)

根据你的说明,大写字母导出规则不适用于嵌套结构体的字段,对吗?因此,我可以进行如下操作:

var S Stratification
S.nh = []int{5, 6, 7}

即使nh字段以小写字母开头,S.nh仍然会被导出。但若我尝试导入Sample.nh字段,这种方式是否将失效?如有理解错误请指正。

在Go中处理统计公式的命名时,确实需要遵循导出规则,但可以通过结构体和组合来保持清晰性。以下是一个示例实现:

package sampling

// Stratified 表示分层抽样的核心参数
type Stratified struct {
    PopulationN int   // 总体大小
    SampleN     int   // 样本大小
    PopulationNh []int // 各层总体大小
    SampleNh    []int // 各层样本大小
}

// NewStratified 创建分层抽样参数
func NewStratified(popN, sampN int, popNh, sampNh []int) *Stratified {
    return &Stratified{
        PopulationN:  popN,
        SampleN:      sampN,
        PopulationNh: popNh,
        SampleNh:     sampNh,
    }
}

// 内部计算可以使用短变量名
func (s *Stratified) calculate() float64 {
    // 在方法内部可以自由使用公式中的原始符号
    N := s.PopulationN
    n := s.SampleN
    Nh := s.PopulationNh
    nh := s.SampleNh
    
    // 示例计算:加权平均
    var sum float64
    for i := range Nh {
        sum += float64(nh[i]) / float64(Nh[i])
    }
    return sum / float64(n) * float64(N)
}

对于包内部使用,可以创建非导出类型:

// internalSampling 内部实现使用短名称
type internalSampling struct {
    N  int   // 总体大小
    n  int   // 样本大小
    Nh []int // 各层总体大小
    nh []int // 各层样本大小
}

// 导出的包装器
type ExportedSampling struct {
    inner internalSampling
}

func (es *ExportedSampling) PopulationN() int {
    return es.inner.N
}

func (es *ExportedSampling) SampleN() int {
    return es.inner.n
}

另一种方法是使用常量风格:

const (
    N  = "population"
    n  = "sample"
    Nh = "stratum_population"
    nh = "stratum_sample"
)

type Parameters struct {
    PopulationSize      int
    SampleSize         int
    StratumPopulations []int
    StratumSamples     []int
}

在测试中可以直接使用短变量名:

func TestStratifiedSampling(t *testing.T) {
    // 测试代码中可以使用公式原始符号
    N := 1000
    n := 100
    Nh := []int{300, 400, 300}
    nh := []int{30, 40, 30}
    
    // ... 测试逻辑
}

对于公式密集的代码区域,可以使用局部变量保持可读性:

func (s *Stratified) computeVariance() float64 {
    // 临时变量使用公式符号
    N := s.PopulationN
    n := s.SampleN
    Nh := s.PopulationNh
    nh := s.SampleNh
    
    // 复杂的公式计算
    var total float64
    for h := range Nh {
        Wh := float64(Nh[h]) / float64(N)
        fh := float64(nh[h]) / float64(Nh[h])
        total += Wh * Wh * fh * (1 - fh) / float64(nh[h])
    }
    return total
}
回到顶部