在Go语言中为函数预计算简单值

huangapple go评论84阅读模式
英文:

Precomputing simple values for a function in Go

问题

我刚刚开始使用Go进行一个简单的Web编程项目,但我无法弄清楚如何在单个函数中实现简单的预计算。在OCaml中,我经常这样做:

(* maybe render_page is a handler function for an HTTP server or something )
let render_page =
(
let's say that Template.prepare takes a template string and returns its compiled representation )
let templ = Template.prepare "user: {{.}}, group: {{.}}" in
(
return an anonymous function *)
fun user group ->
templ [user; group]

对于不熟悉OCaml的人来说,上面的代码是将名称render_page绑定到一个接受两个参数并返回网页的函数,但在内部,我首先创建了一个局部绑定到templ(此绑定仅在render_page的定义中可见,并且计算仅发生一次),然后在匿名函数中使用该绑定,该匿名函数是实际绑定到render_page的值。因此,当你调用render_page时,templ不会每次重新编译:它只是从闭包环境中获取。

在Go中是否有一种常见的模式可以实现类似的功能?我想尽量避免使用全局变量。我知道“全局”变量可能被限制在包的命名空间中,这是我目前正在做的,但我希望将这些预计算表达式的可见性限制在它们所需的函数中。

谢谢!

英文:

I just started using Go for a simple web programming project, but I can't quite figure out how to accomplish simple pre-computation local to a single function. This is something I do quite frequently in OCaml, e.g.:

(* maybe render_page is a handler function for an HTTP server or something *)
let render_page =
  (* let's say that Template.prepare takes a template string and returns its compiled representation *)
  let templ = Template.prepare "user: {{.}}, group: {{.}}" in
  (* return an anonymous function *)
  fun user group ->
    templ [user; group]

For those unfamiliar with OCaml, what's happening above is I'm binding the name render_page to a function that takes two parameters and presumably returns a web page, but internally I'm first creating a local binding to templ (this binding is only visible within the definition of render_page, and the computation only happens once) and then using that binding within an anonymous function, which is the actual value bound to render_page. So when you call render_page, templ isn't recompiled every time: it's just fetched from the closure environment.

Is there a common pattern for accomplishing something like this in Go? I'd like to avoid global variables as much as possible. I'm aware that "global" variables may be confined to a package's name space, which is what I'm currently doing, but I'd like to restrict the visibility of these precomputed expressions to just the functions in which they're needed.

Thanks!

答案1

得分: 1

对于不熟悉Ocaml的人来说,我不能百分之百确定这个Go示例是否符合你的要求,但你可以在Go中定义一个函数,该函数可以预先计算一些内容,然后返回一个内部使用预先计算值的匿名函数。

例如,你可以这样做:

func matcher(pattern string) func(string) bool {
    regExp := regexp.MustCompile(pattern)
    return func(s string) bool {
        return regExp.MatchString(s)
    }
}

然后通过以下方式创建一个这样的函数:

myMatcher := matcher("123")

你可以多次调用myMatcher("something"),由于在调用matcher函数时已经编译了正则表达式,所以正则表达式不会每次都重新编译。

这里有一个使用这种方法的Go Playground示例:

https://play.golang.org/p/m3vBrYn4Dg

英文:

Not familiar with Ocaml so not 100% sure if this Go example is what you are looking for, but you can define a function in Go which can pre-calculate some stuff and then return an anonymous function that internally uses the pre-calculated values.

For example, if you do this:

func matcher(pattern string) func(string) bool {
    regExp := regexp.MustCompile(pattern)
    return func(s string) bool {
	    return regExp.MatchString(s)
    }
}

And then create one of these functions by doing:

myMatcher := matcher("123")

You can then call myMatcher("something") multiple times, and the regexp expression will not be compiled each time since it was already compiled when calling the matcher function.

Here's a working Go playground with this:

https://play.golang.org/p/m3vBrYn4Dg

答案2

得分: 0

我认为闭包(类似于@eugenioy已经回答的内容)是你能得到的最接近的东西。

func main() {
    userGroupTemplate := renderPage()
    fmt.Println(userGroupTemplate("sberry", "StackOverflow"))
    fmt.Println(userGroupTemplate("user1030453", "StackOverflow"))
}

func renderPage() func(user, group string) string {
    templ := "user: %s, group: %s"
    fmt.Println("template is created")
    return func(user, group string) string {
        return fmt.Sprintf(templ, user, group)
    }
}

输出

template is created
user: sberry, group: StackOverflow
user: user1030453, group: StackOverflow
英文:

I think a closure (similar to what @eugenioy already answered with) is the closest you are going to get.

func main() {
	userGroupTemplate := renderPage()
	fmt.Println(userGroupTemplate("sberry", "StackOverflow"))
	fmt.Println(userGroupTemplate("user1030453", "StackOverflow"))
}

func renderPage() func(user, group string) string {
	templ := "user: %s, group: %s"
	fmt.Println("template is created")
	return func(user, group string) string {
		return fmt.Sprintf(templ, user, group)
	}
}

OUTPUT

template is created
user: sberry, group: StackOverflow
user: user1030453, group: StackOverflow

答案3

得分: 0

除了这里其他的答案之外,还要注意一旦你定义了一个闭包,你可以将其绑定到一个全局变量,并像其他函数一样对待它。

这里是eugenioy的答案稍作修改:

var scan = matcher("123")

func main() {
    fmt.Println(scan("456"))
    fmt.Println(scan("123"))
}

在这里,scan 是一个全局闭包,非常接近你在OCaml中所做的。

链接:https://play.golang.org/p/Lc0SC53b-0

英文:

In addition to the other answers here, note that once you define a closure, you can bind it to a global variable and treat it like other functions.
Here is eugenioy's answer slightly modified :

var scan = matcher("123")

func main() {
	fmt.Println(scan("456"))
	fmt.Println(scan("123"))
}

Here, scan is a global closure, which is very close to what you did in OCaml.

https://play.golang.org/p/Lc0SC53b-0

huangapple
  • 本文由 发表于 2017年6月3日 04:36:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/44337071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定