標籤:

為什麼 Scala 不建議用 return?

剛開始學 編譯的時候會報warning :

Avoid using return

有return的話 讀代碼的時候不會覺得更清晰么

stackoverflow上也有相同的問題 不過沒太看懂


因為不清真(purity)


問 Haskell 那邊的,你就會被教育的很徹底了


實際上,有 return 的話,代碼反而會變得不清晰。

簡單地講,函數式編程差不多追求的就是代碼塊(函數)寫得和數學表示差不多,因為這樣才叫清晰。比如在數學中:

f(x) = x + 1

對應成Scala代碼,差不多就是:

def f(x: Double) = x + 1

除了 def 關鍵字和對參數的類型標記外,是完全一樣的。

除了這種視覺上的不清晰,計算上也不清晰。如果是純函數式(Pure function)的,是可以應用替換模型(?我不太確定,希望有人能補充)的。比如再有一個定義:

def g(y: Double, z: Double) = f(y) * f(z)

那麼就可以這樣證明 g 是幹什麼的(?數理邏輯有形式化的方法,但是我早就忘光了,大概演示一下,意思意思,希望有人能補充):

g(y, z) = f(y) * f(z)

=&> g(y, z) = f(x|y) * f(x|z) // 可以理解為*號左邊的調用用y替代f的形式參數x,右邊的調用用z替代f的形式參數y

=&> g(y, z) = (x|y + 1) * (x|z + 1) //應用f

=&> g(y, z) = (y + 1) * (z + 1) // 替代

=&> g(y, z) = y * z + y + z + 1

如果你用了 return ,你試試還能怎麼證明 g 的正確性?不但難以證明,而且可能連 g 是幹什麼的都看不出來(上面這個例子太簡單,我相信題主在實踐中已經見過了不看函數名和注釋就不知道是幹什麼的的函數)。補充:為什麼需要證明呢?因為你要確保 g 正確。確保正確性,我知道的有兩種辦法:一是試驗,列舉出所有的合法輸入,然後看結果是否都是符合預期的,這個目前看來不容易做到,比如上面的例子中你能列舉出所有的 Double 類型值嗎?二是證明,這個對程序本身有要求,還是參考Pure function。現在的軟體測試一般是屬於第一種方法,只不過使用了劃分子集、取邊界條件等等方法來降低難度,但是這樣的話只能發現錯誤,不能證明正確性。

最後一點就是 return 在函數式編程中實際上是有特殊意義的,簡單地講(複雜的我也不會)就是 return 應該是"monad"(Monad (functional programming))的unit function,用Scala寫出來應該是這樣(這是偽代碼,沒有試過能不能編譯、是否正確,如果有問題,純屬意料之中):

trait Monad[T[_]] {
def return[A: T](a: A): T[A]
def flatMap[A: T, B: T](a: T[A])(f: A =&> T[B]): T[B]
}

return 應該終結執行,直接返回結果,在 Scala 中是拋出異常然後 catch 。你寫程序時寫的

def h(x: Double) = {
val a = f(x)
val b = f(2)
val c = f(a)
f(c)
}

在函數式語言中,這是語言提供的特殊語法,將下面這個難看的東西給包裝了一下(依然是偽代碼,如果確實是這樣,那一定是Scala編譯器沒做優化):

def h(x: Double) =
try(f(x)).flatMap((a: Double) =&>
try(f(2)).flatMap((b: Double) =&>
try(f(a)).flatMap((c: Double) =&>
f(c)
))) match {
case Success(value) =&> value
case Failure(IAmAF_ckingReturnValue(value)) =&> value
case Failure(t) =&> throw t
}

可以參考這門公開課 Coursera - Free Online Courses From Top Universities 以及這門課中的這份PPT https://d396qusza40orc.cloudfront.net/reactive/lecture_slides/week3-1.pdf。我猜(只能用這個字了) return 拋出異常後, try 返回 Failure ,然後逐級傳遞上去。當然,這些實現細節都是我YY出來的,歡迎各位補充。我只是拋磚引玉罷了。

關於 return 語句,詳見Scala語言規範文檔Expressions,並仔細看看最後一段,摘抄如下:

If the return expression is itself part of an anonymous function, it is possible that the enclosing instance of fhas already returned before the return expression is executed. In that case, the thrownscala.runtime.NonLocalReturnException will not be caught, and will propagate up the call stack.

所以 return 還是不要用為好。

2016年7月22日更新:

有同事在代碼中用了return語句,發現性能很差,移除掉return後性能有顯著提升。


和Ruby類似,Scala中將最後出現的變數作為return的值。

使用return必須顯式指定返回類型,使Scala失去推斷返回值類型的能力。

因此,沒必要使用return關鍵字了。

而必須使用return的代碼往往意味著存在「多路徑返回」的問題,即return存在多個條件分支中,並藉助return中斷返回的特性來處理分支。《代碼大全》中不建議這麼做。因為

1 可讀性往往不好

2 並不是非要這麼做

而在Scala中,鼓勵使用函數式的風格更加讓Return顯得沒有用武之地。

因此,如果發現不得不使用return的時候,應該重新審視下流程是否應該改寫,過多的嵌套循環是否能拆分成多個函數遞歸,lambda等形式


沒有必要的就不用寫上


我認為是為了避免歧義,比如下面的代碼結果是什麼?:

BTW:下面的例子某種程度上也解釋了為什麼函數內含return的時候,一定要指名函數返回類型。

scala&> def f(xs: List[Int]): Any = return xs.map(x =&> return x*x)
f: (xs: List[Int])Any

scala&> f(List(1,2,3)) // =&> ??

想了想,上面的例子太簡單了,沒有說服力,下面來一個稍微複雜點的:

scala&> def value: Int = {
| def one(x: Int): Int = { return x; 1 }
| val two = (x: Int) =&> { return x; 2 }
| 1 + one(2) + two(3)
| }
value: Int

scala&> value // =&> ??


1. Scala 是一門函數式編程語言。函數式編程語言中所有東西都是表達式而不是語句。return 是一種「命令」,命令電腦返回這個值。而表達式是聲明這個表達式的最終計算結果是這個值。

2. Scala 的 return 的語義跟我們平時理解的返回一個值已經不一樣了。它的意思是:退出包含 return 的最近的一個方法(不是函數),方法是用 def 定義的。詳見:http://tpolecat.github.io/2014/05/09/return.html


兩個原因:

1. 迴避「副作用」.

2. 使用「表達式(expression)」,而非「語句(statement)」.

使用return意味著你可以在函數的任意位置結束函數。這樣會產生「副作用」,函數沒有因為邏輯分支而自然結束。

使用return表明你的函數是一個產生值的語句塊,而不是一個值。前者使你的代碼成為語句,後者使之成為表達式。

這只是代碼風格的問題,其實不是什麼大問題,想用return大可放心使用。

但是既然選擇使用Scala,就最好遵循它的GuideLine,使用表達式來書寫代碼。

對於這個問題比較困惑的話,可以觀看一下視頻的前半部分。

簡單易懂,絕不騙鬼:

https://www.youtube.com/watch?v=IobLWVuD-CQlist=WLindex=1


Scala是函數式語言,沒有語句,只有函數,一個for{}是一個函數,一個if else{}也是函數,函數自然就有值。


Scala見到return就會跳出方法。至少,它就會影響到推演返回類型的能力!

語法上,對於使用了return的方法,就需要顯式提供返回類型。


推薦閱讀:

國內有哪些 Scala 大牛?
如何评价Functional Programming in Scala 这本书?
如果對方是一個 Scala 愛好者,有什麼辦法說服他使用 Go?
Node.js、Scala、Clojure 等聲稱適合高並發的語言,分別具體適用於什麼情景,何種任務?
請問各位大神,spark的ML和MLLib兩個包區別和聯繫?!?

TAG:Scala |