2015-02-17 66 views
7

爲什麼我不能將遞歸函數定義爲變量?我似乎可以定義任意函數,除非它們遞歸。Golang函數和函數變量語義

這是合法的:

func f(i int) int { 
    if i == 0 { 
     return 1 
    } 
    return i * f(i-1) 
} 

func main() { 
    fmt.Println(f(2)) 
} 

這是非法的:

var f func(int) int = func(i int) int { 
    if i == 0 { 
     return 1 
    } 
    return i * f(i-1) 
} 

func main() { 
    fmt.Println(f(2)) 
} 

這是合法的,我猜這是隻是因爲你可以計算輸出F初始化後:

func main() { 
    var f *func(int) int; 
    t := func(i int) int { 
     if i == 0 { 
      return 1 
     } 
     return i * (*f)(i-1) 
    } 
    f = &t 
    fmt.Println((*f)(2)) 
} 

所以它看起來像是函數和函數類型的變量聲明被處理不同儘管從閱讀文檔來看,我並不認爲情況會如此。我錯過了詳細說明這部分的文檔嗎?

我希望這個非法案件能夠正常工作,只是因爲它在其他語言中起作用。像在JavaScript中一樣:

(function() { 
    var f = function (i) { 
    if (i == 0) { 
     return 1; 
    } 
    return i * f(i - 1); 
    }; 

    console.log(f(2)); 
})(); 
+3

圍棋是沒有的JavaScript。 Javascript看着你的代碼,並說「看起來不錯,我不知道'f'是什麼,但我可能會在這個塊被調用時」,然後把函數賦給變量'f',並且一切都很好。 Go在遇到調用它的代碼之前想知道'f'是什麼。 – 2015-02-17 22:50:17

+0

這就是發生了什麼,但並沒有真正說明原因。如果'f'被聲明爲一個函數,Go不想知道'f'是什麼,但是Go確實想知道'f'是否被聲明爲一個函數類型變量。這是奇怪的。 – 2015-02-17 23:00:20

回答

21

下面的代碼將是您描述的首選方式。請注意,您不必創建一個額外的變量,也不是你有指針一個函數:

package main 

import "fmt" 

func main() { 
    var f func(int) int 
    f = func(i int) int { 
     if i == 0 { 
      return 1 
     } 
     return i * f(i-1) 
    } 
    fmt.Println(f(2)) 
} 
+0

感謝這是一個更好的工作解決方案。我希望有一些文件指出這一點。看起來很奇怪,函數類型的函數和變量被如此區別對待。 – 2015-02-17 23:01:25

+2

@DanielWilliams:從[spec](https://golang.org/ref/spec#Declarations_and_scope):*在函數內聲明的常量或變量標識符的範圍從ConstSpec或VarSpec的末尾開始(ShortVarDecl for短變量聲明)並結束於最內層包含塊的末尾。*這意味着在完全定義值之前,f標識符是無效的。這就是爲什麼你不能在Go中做你在JavaScript中做的事情。 – 2015-02-18 00:58:45