Go 函数

函数是 Go 的核心。Go 函数使用 func 开头,可以使用零或多个参数,可以返回一个参数,或者使用元组包装多个参数。Go 需要明确的 return,也就是说,它不会自动 return 最后一个表达式的值。

当多个连续的参数为同样类型时,可以仅声明最后一个参数的类型,忽略之前相同类型参数的类型声明。

Go 原生支持多返回值,如果需要返回多个值,可以使用元组来表示,例如(int,int)在函数中表示返回 2 个 int。在调用函数时,如果仅仅需要返回值的一部分的话,可以使用空白标识符_。

可变参数函数。在调用时可以传入任意数量的参数。使用…来表示,例如:

func sum(nums ...int) {
	// 实现逻辑
}

这个函数可以接受任意数量的 int 作为参数。

Go 支持匿名函数,并能用其构造闭包。匿名函数在你想定义一个不需要命名的内联函数时非常有用。

func intSeq() func() int{
	i:=0
	return func() int{
		i++
		return i
	}
}

intSeq 函数返回一个在其函数体内定义的匿名函数。返回的函数使用闭包的方式隐藏变量 i。返回的函数隐藏变量 i 以形成闭包。

我们可以这样调用:

nextInt := intSeq()
fmt.Println(nextInt())
fmt.Println(nextInt())

我们调用 intSeq 函数,将返回值(一个函数)赋给 nextInt。这个函数的值包含了自己的值 i,这样在每次调用 nextInt 时,都会更新 i 的值。

Go 支持递归,例如:

func fact(n int) int {
    if n == 0 {
        return 1
    }
    return n * fact(n-1)
}

这个 fact 函数在到达 fact(0)前一直调用自身。

闭包也是可以递归的,但这要求在定义闭包之前用类型化的 var 显式声明闭包。例如这样:

var fib func(n int) int

fib = func(n int) int {
    if n < 2 {
        return n
    }
    return fib(n-1) + fib(n-2)
}
fmt.Println(fib(7))

如果需要先定义,然后在 return 前返回需要添加 defer 关键字。

下面的一段代码,获取几个整型参数经计算后再返回一个整型值:

package main

import (
	"math/bits"
)

// 相同的质数表(确保与Rust一致)
var primes = [4]int64{
	0x7F4A7C15, // 32位质数
	0x9E3779B9, // 黄金分割质数
	0xABCDEF01, // 自定义大质数
	0x123456789ABCDEF, // 64位质数
}

// 计算d值(与Rust完全一致)
func CalculateD(a, b, nonce, deviceID int64) int64 {
	// 1. 动态选择质数(错误写法)
	prime := primes[(a^deviceID)%4]
	if (a^deviceID)%4 < 0 { // 处理负数取模
		prime = primes[((a^deviceID)%4)+4]
	}
    // 1. 安全选择质数
    index := (a ^ deviceID) % 4
    if index < 0 {
        index += 4
    }
    prime := primes[index]

	// 2. 多阶段混淆计算
	stage1 := a * prime ^ nonce            // 乘法+异或
	stage2 := rotateLeft(b+deviceID, 17)   // 加法+循环左移
	stage3 := (stage1 + stage2) * prime   // 混合叠加

	// 3. 约束到正数范围(同Rust的% 0x7FFFFFFFFFFFFFFF)
	return stage3 & 0x7FFFFFFFFFFFFFFF
}

// 循环左移(Go无内置操作,需手动实现)
func rotateLeft(n int64, shift uint) int64 {
	shift %= 64
	return (n << shift) | (n >> (64 - shift))
}

// 验证函数(比较Rust和Go的结果)
func Verify(a, b, nonce, deviceID int64, rustResult int64) bool {
	return CalculateD(a, b, nonce, deviceID) == rustResult
}

还有一个比较特殊的是函数闭包,Go 函数可以是闭包。闭包是指引用了其外部作用域变量的函数值。该函数可以访问并修改这些引用的变量;从这个意义上说,函数与这些变量是“绑定”的。

package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

通关密语:func