英文:
First class functions in Go
问题
我来自JavaScript,它支持一级函数。例如,你可以:
- 将一个函数作为参数传递给另一个函数
- 从一个函数中返回一个函数。
有人可以给我一个在Go中如何实现这个的例子吗?
英文:
I come from JavaScript which has first class function support. For example you can:
- pass a function as a parameter to another function
- return a function from a function.
Can someone give me an example of how I would do this in Go?
答案1
得分: 42
Go语言和函数式编程可能会有所帮助。以下是这篇博客文章的内容:
package main
import fmt "fmt"
type Stringy func() string
func foo() string{
return "Stringy function"
}
func takesAFunction(foo Stringy){
fmt.Printf("takesAFunction: %v\n", foo())
}
func returnsAFunction()Stringy{
return func()string{
fmt.Printf("内部的stringy函数\n");
return "bar" // 必须返回一个字符串才能是stringy类型
}
}
func main(){
takesAFunction(foo);
var f Stringy = returnsAFunction();
f();
var baz Stringy = func()string{
return "匿名的stringy\n"
};
fmt.Printf(baz());
}
作者是博客所有者:Dethe Elza(不是我)
英文:
Go Language and Functional Programming might help. From this blog post:
package main
import fmt "fmt"
type Stringy func() string
func foo() string{
return "Stringy function"
}
func takesAFunction(foo Stringy){
fmt.Printf("takesAFunction: %v\n", foo())
}
func returnsAFunction()Stringy{
return func()string{
fmt.Printf("Inner stringy function\n");
return "bar" // have to return a string to be stringy
}
}
func main(){
takesAFunction(foo);
var f Stringy = returnsAFunction();
f();
var baz Stringy = func()string{
return "anonymous stringy\n"
};
fmt.Printf(baz());
}
Author is the blog owner: Dethe Elza (not me)
答案2
得分: 26
package main
import (
"fmt"
)
type Lx func(int) int
func cmb(f, g Lx) Lx {
return func(x int) int {
return g(f(x))
}
}
func inc(x int) int {
return x + 1
}
func sum(x int) int {
result := 0
for i := 0; i < x; i++ {
result += i
}
return result
}
func main() {
n := 666
fmt.Println(cmb(inc, sum)(n))
fmt.Println(n * (n + 1) / 2)
}
英文:
package main
import (
"fmt"
)
type Lx func(int) int
func cmb(f, g Lx) Lx {
return func(x int) int {
return g(f(x))
}
}
func inc(x int) int {
return x + 1
}
func sum(x int) int {
result := 0
for i := 0; i < x; i++ {
result += i
}
return result
}
func main() {
n := 666
fmt.Println(cmb(inc, sum)(n))
fmt.Println(n * (n + 1) / 2)
}
output:
222111
222111
答案3
得分: 6
从规范中提取的相关部分:函数类型。
这里的其他答案首先声明了一个新类型,这是很好的做法,可以使代码更易读,但要知道这不是必需的。
您可以在不声明新类型的情况下使用函数值,如下面的示例所示。
声明一个具有两个float64
类型参数和一个返回值为float64
类型的函数类型的变量如下所示:
// 创建一个具有上述函数类型的变量:
var f func(float64, float64) float64
让我们编写一个返回加法函数的函数。这个加法函数应该接受两个float64
类型的参数,并在调用时返回这两个数字的和:
func CreateAdder() func(float64, float64) float64 {
return func(x, y float64) float64 {
return x + y
}
}
让我们编写一个具有3个参数的函数,前两个参数是float64
类型,第三个参数是一个函数值,一个接受两个float64
类型输入参数并产生float64
类型值的函数。我们正在编写的函数将调用作为参数传递给它的函数值,并使用前两个float64
值作为函数值的参数,并返回传递的函数值返回的结果:
func Execute(a, b float64, op func(float64, float64) float64) float64 {
return op(a, b)
}
让我们看看我们之前的示例的运行情况:
var adder func(float64, float64) float64 = CreateAdder()
result := Execute(1.5, 2.5, adder)
fmt.Println(result) // 输出 4
请注意,当创建adder
时,您当然可以使用短变量声明:
adder := CreateAdder() // adder 的类型为:func(float64, float64) float64
在Go Playground上尝试这些示例。
使用现有函数
当然,如果您已经在一个包中声明了具有相同函数类型的函数,您也可以使用它。
例如,math.Mod()
具有相同的函数类型:
func Mod(x, y float64) float64
因此,您可以将此值传递给我们的Execute()
函数:
fmt.Println(Execute(12, 10, math.Mod)) // 输出 2
输出为2
,因为12 mod 10 = 2
。请注意,现有函数的名称充当函数值。
在Go Playground上尝试它。
注意:
请注意,参数名称不是类型的一部分,具有相同参数和结果类型的两个函数的类型是相同的,而不管参数的名称如何。但要知道,在参数或结果列表中,参数的名称必须全部存在或全部不存在。
因此,例如,您也可以编写:
func CreateAdder() func(P float64, Q float64) float64 {
return func(x, y float64) float64 {
return x + y
}
}
或者:
var adder func(x1, x2 float64) float64 = CreateAdder()
英文:
The related section from the specification: Function types.
All other answers here first declare a new type, which is good (practice) and makes your code easier to read, but know that this is not a requirement.
You can work with function values without declaring a new type for them, as seen in the below example.
Declaring a variable of function type which has 2 parameters of type float64
and has one return value of type float64
looks like this:
// Create a var of the mentioned function type:
var f func(float64, float64) float64
Let's write a function which returns an adder function. This adder function should take 2 parameters of type float64
and should returns the sum of those 2 numbers when called:
func CreateAdder() func(float64, float64) float64 {
return func(x, y float64) float64 {
return x + y
}
}
Let's write a function which has 3 parameters, first 2 being of type float64
, and the 3rd being a function value, a function that takes 2 input parameters of type float64
and produces a value of float64
type. And the function we're writing will call the function value that is passed to it as parameter, and using the first 2 float64
values as arguments for the function value, and returns the result that the passed function value returns:
func Execute(a, b float64, op func(float64, float64) float64) float64 {
return op(a, b)
}
Let's see our previous examples in action:
var adder func(float64, float64) float64 = CreateAdder()
result := Execute(1.5, 2.5, adder)
fmt.Println(result) // Prints 4
Note that of course you can use the Short variable declaration when creating adder
:
adder := CreateAdder() // adder is of type: func(float64, float64) float64
Try these examples on the Go Playground.
Using an existing function
Of course if you already have a function declared in a package with the same function type, you can use that too.
For example the math.Mod()
has the same function type:
func Mod(x, y float64) float64
So you can pass this value to our Execute()
function:
fmt.Println(Execute(12, 10, math.Mod)) // Prints 2
Prints 2
because 12 mod 10 = 2
. Note that the name of an existing function acts as a function value.
Try it on the Go Playground.
Note:
Note that the parameter names are not part of the type, the type of 2 functions having the same parameter and result types is identical regardless of the names of the parameters. But know that within a list of parameters or results, the names must either all be present or all be absent.
So for example you can also write:
func CreateAdder() func(P float64, Q float64) float64 {
return func(x, y float64) float64 {
return x + y
}
}
Or:
var adder func(x1, x2 float64) float64 = CreateAdder()
答案4
得分: 1
虽然你可以使用var或声明类型,但你不需要这样做。
你可以很简单地这样做:
package main
import "fmt"
var count int
func increment(i int) int {
return i + 1
}
func decrement(i int) int {
return i - 1
}
func execute(f func(int) int) int {
return f(count)
}
func main() {
count = 2
count = execute(increment)
fmt.Println(count)
count = execute(decrement)
fmt.Println(count)
}
//输出结果为:
3
2
英文:
While you can use a var or declare a type, you don't need to.
You can do this quite simply:
package main
import "fmt"
var count int
func increment(i int) int {
return i + 1
}
func decrement(i int) int {
return i - 1
}
func execute(f func(int) int) int {
return f(count)
}
func main() {
count = 2
count = execute(increment)
fmt.Println(count)
count = execute(decrement)
fmt.Println(count)
}
//The output is:
3
2
答案5
得分: 0
只是一个使用递归函数定义的脑筋急转弯,用于在Web应用程序中链接中间件。
首先,是工具箱:
func MakeChain() (Chain, http.Handler) {
nop := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {})
var list []Middleware
var final http.Handler = nop
var f Chain
f = func(m Middleware) Chain {
if m != nil {
list = append(list, m)
} else {
for i := len(list) - 1; i >= 0; i-- {
mid := list[i]
if mid == nil {
continue
}
if next := mid(final); next != nil {
final = next
} else {
final = nop
}
}
if final == nil {
final = nop
}
return nil
}
return f
}
return f, final
}
type (
Middleware func(http.Handler) http.Handler
Chain func(Middleware) Chain
)
如你所见,类型Chain
是一个返回相同类型Chain
的另一个函数(多么一流的类型!)。
现在进行一些测试以查看其运行情况:
func TestDummy(t *testing.T) {
c, final := MakeChain()
c(mw1(`OK!`))(mw2(t, `OK!`))(nil)
log.Println(final)
w1 := httptest.NewRecorder()
r1, err := http.NewRequest("GET", "/api/v1", nil)
if err != nil {
t.Fatal(err)
}
final.ServeHTTP(w1, r1)
}
func mw2(t *testing.T, expectedState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val := r.Context().Value(contextKey("state"))
sval := fmt.Sprintf("%v", val)
assert.Equal(t, sval, expectedState)
})
}
}
func mw1(initialState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), contextKey("state"), initialState)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
type contextKey string
再次强调,这只是一个脑筋急转弯,展示了我们可以在Go中以不同的方式使用一流函数。个人而言,我现在使用chi作为路由器和处理中间件。
英文:
Just a brainteaser with recursive function definition for chaining middlewares in a web app.
First, the toolbox:
func MakeChain() (Chain, http.Handler) {
nop := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {})
var list []Middleware
var final http.Handler = nop
var f Chain
f = func(m Middleware) Chain {
if m != nil {
list = append(list, m)
} else {
for i := len(list) - 1; i >= 0; i-- {
mid := list[i]
if mid == nil {
continue
}
if next := mid(final); next != nil {
final = next
} else {
final = nop
}
}
if final == nil {
final = nop
}
return nil
}
return f
}
return f, final
}
type (
Middleware func(http.Handler) http.Handler
Chain func(Middleware) Chain
)
As you see type Chain
is a function that returns another function of the same type Chain
(How first class is that!).
Now some tests to see it in action:
func TestDummy(t *testing.T) {
c, final := MakeChain()
c(mw1(`OK!`))(mw2(t, `OK!`))(nil)
log.Println(final)
w1 := httptest.NewRecorder()
r1, err := http.NewRequest("GET", "/api/v1", nil)
if err != nil {
t.Fatal(err)
}
final.ServeHTTP(w1, r1)
}
func mw2(t *testing.T, expectedState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val := r.Context().Value(contextKey("state"))
sval := fmt.Sprintf("%v", val)
assert.Equal(t, sval, expectedState)
})
}
}
func mw1(initialState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), contextKey("state"), initialState)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
type contextKey string
Again, this was just a brainteaser to show we can use first class functions in Go in different ways. Personally I use chi nowadays as router and for handling middlewares.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论