2016年6月30日 星期四

[Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (物件相等性)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 的繼承作了一些限制,這使你在使用繼承前必須多一份思考. 

物件相等性 : 
在 Scala 中,如果要比較兩個物件的實質相等性,可以使用 == 或 !=,例如 : 
  1. val s1 = new String("Java")  
  2. val s2 = new String("Java")  
  3. println(s1 == s2)       // 顯示 true  
  4. println(s1 eq s2)       // 顯示 false  
雖然兩個物件是新建構出來的,s1 與 s2 是參考到不同物件,但在 Scala 中使用 == 是比較兩個字串的實質字元序列,所以結果會是 true,在 Scala 中若要測試兩個參考是否為同一物件,則可以使用 eq 或 ne 方法== 與 != 方法有兩個版本,一個版本是定義在scala.Any 中,Any 是 Scala 中所有類別的最頂層父類別,其中 == 與 != 的方法定義為 : 
final def ==(arg0 : Any) : Boolean
final def !=(arg0 : Any) : Boolean

在 API 文件說明中表示,== 的作用等於 equals() 的作用 : 
def equals(arg0 : Any) : Boolean

equals() 的作用是測試兩個 args0 的作用與 this 是否參考同一物件. 而 != 等於 == 的反相結果,也就是 o != arg0 等於 !(o == (arg0)). 事實上,當你在 Scala 中定義一個類別時沒有明確指定父類別,則會繼承 scala.AnyRef相當於 Java 的 java.lang.Object),這是 Any 的直接子類別。AnyRef 定義了== 與 !=,其版本為 : 
final def ==(arg0 : AnyRef) : Boolean
final def !=(arg0 : AnyRef) : Boolean

!=等於==的反相結果,而==的定義內容相當於 : 
  1. final def == (arg0 : Any): Boolean =   
  2.     if (this eq null) arg0 eq null else this.equals(arg0)  
eq 與 ne 方法,是定義在 AnyRef 中(在 Scala 中,像 1 這樣的物件是 scala.AnyVal 的實例,[color=red]AnyVal 是 Any 的直接子類別,所以 scala.AnyVal 沒有 eq  ne 方法,scala.AnyVal 物件只要值相同,一定是同一個物件實例,也就是 1 equals 1結果一定是 true): 
final def eq(arg0 : AnyRef) : Boolean
final def ne(arg0 : AnyRef) : Boolean

ne 為 eq 的反相結果,而 eq 主要在測試目前物件與 arg0 所參考的物件是否為同一物件. 無論是 Any 中的 ==、!= 或 AnyRef 中的 ==!=eqne 方法,都被宣告為 final,你沒辦法在子類別中重新定義(AnyRef 被宣告為 final,你沒辦法繼承),別以為以下是重新定義 == 方法 : 
  1. class Point(val x: Int, val y: Int) {  
  2.     def ==(that: Point) = this.x == that.x && this.y == that.y  
  3. }  
  4.   
  5. val p1 = new Point(11)  
  6. val p2 = new Point(11)  
  7. println(p1 == p2)         // 顯示 true  
看來好像是對的,但是如果你這麼測試 : 
val p1: AnyRef = new Point(1, 1)
val p2 = new Point(1, 1)
println(p1 == p2)
 // 顯示 false

事實上是,你沒有重新定義 Any 或 AnyRef f的 == 方法,你是定義了一個新的 == 方法,而直接繼承了 Any 與 AnyRef f的 == 方法,所以之前顯示 true 是因為方法被重載了,而你使用的是接受 Point 參數的 == 版本. 因為 Any 與 AnyRef 的 ==!= 都被宣告為 final,你不能重新定義 ==、!= 方法來定義自己的物件相等性。依以上的說明,無論是哪個版本,!= 一定是 == 的反相結果,而 == 都會呼叫 equals() 方法,所以結論是,要定義物件相等性,請重新定義 equals() 方法跟 Java 相同). 
如何正確定義 equals() 需要作些討論,如果你熟悉 Java,可以先看看 Java 物件相等性 中的說明,如果你想要知道如何以 Scala 語法正確定義 equals() 方法,則可以看看 重新定義 equals() 方法

補充說明 : 
[ Java 小學堂 ] Java 世界裡 equals 與 == 的差別

[Scala IA] The Basics : Ch2. Getting Started - Working with Array and List

Working with Array and List 
Chapter 4 is dedicated to data structures, but until then I’ll introduce List and Array so you can start writing useful Scala scripts. In Scala, array is an instance of the scala.Array class and is similar to the Java array: 
scala> val array = new Array[String](3)
array: Array[String] = Array(null, null, null)

scala> array(0) = "This"

scala> array(1) = "is"

scala> array(2) = "mutable"

scala> array
res3: Array[String] = Array(This, is, mutable)

Always remember that in Scala the type information or parameterization is provided using square brackets. The type parameterization is a way to configure an instance with type information when creating the instance. Now iterating through each element in the array is easy; call the foreach method: 
scala> array.foreach(e => println(e))
This
is
mutable


scala> array.foreach(println)
This
is
mutable

You’re passing a function literal to the foreach method to print all the elements in the array. There are other useful methods defined for Array objects; for a complete list look in Scaladoc for scala.collection.mutable.ArrayLike. The most obvious question in your mind right now is probably why we have to check ArrayLike, which is a different class than the Array used to check methods that are available for an Array instance in Scala. The answer is Predef. ScalaPredef provides additional array functionality dynamically using ArrayLike when you create an instance of an Array. Again, Predef is a great place to start to understand the Scala Library. 
NOTE 
Predef implicitly converts Array to scala.collection.mutable.ArrayOpsArrayOps is the subclass of ArrayLike, so ArrayLike is more like the interface for all the additional methods available to Array type collections.

When writing Scala scripts, you sometimes have to take command-like arguments. You can do that implicitly as a val type variable called args. In the following example you’ll create a Scala script that takes input from a user and prints it to the console: 
  1. args.foreach(println)  
Open your favorite editor and save this line in a file called myfirstScript.scala. Open a command prompt to the location where the file is saved and run the following command: 
# scala myfirstScript.scala my first script
my
first
script

The Array is a mutable data structure; by adding each element to the array, you’re mutating the array instance and producing a side effect. In functional programming, methods should not have side effects. The only effect a method is allowed to have is to compute a value and return that value without mutating the instance. An immutable and more functional alternative to a sequence of objects like Array is List. In Scala, List is immutable and makes functional-style programming easy. Creating a list of elements is as easy as the following: 
scala> val myList = List("This", "is", "immutable")
myList: List[java.lang.String] = List(This, is, immutable)

The List is shorthand for scala.collection.immutable.List, and again Predef automatically makes it available to you: 
scala> val myList = scala.collection.immutable.List("This", "is", "immutable")
myList: List[String] = List(This, is, immutable)

scala> myList.getClass
res0: Class[_ <: b="" list="" nbsp="" tring="">class scala.collection.immutable.$colon$colon
 
Most of us are used to mutable collections where, when we add or remove elements, the collection instance expands or shrinks (mutates), but immutable collections never change. Instead, adding or removing an element from an immutable collection creates a new modified collection instance without modifying the existing one. The following example adds an element to an existing List
scala> val oldList = List(1,2)
oldList: List[Int] = List(1, 2)

scala> val newList = 3 :: oldList
newList: List[Int] = List(3, 1, 2)

scala> oldList
res2: List[Int] = List(1, 2)

In this example you’re adding 3 to an existing List containing elements 1 and 2 using the :: method. The job of the :: method is to create a new List with all the existing elements plus the new element added at the front of the List. To add at the end of the List, invoke the :+ method: 
scala> val newList = oldList :+ 3
newList: List[Int] = List(1, 2, 3)

Scala provides a special object called Nil to represent an empty List, and you can use it to create new lists easily: 
scala> val myList = "This" :: "is" :: "immutable" :: Nil
myList: List[String] = List(This, is, immutable)

In this example you’re using a new instance of List every time you add an element. To delete an element from a List, you could use the - method, but it’s deprecated. A better way would be to use the filterNot method, which takes a predicate and selects all the elements that don’t satisfy the predicate. To delete 3 from the newList, you can do something like the following: 
scala> val afterDel = newList.filterNot(_ == 3)
afterDel: List[Int] = List(1, 2)

You’ll delve deeper into Scala collections in chapter 4, section 4.3, but for now you know enough to play with Scala and script a few things. In the meantime I encourage you to look into methods defined for List and play with them.

2016年6月28日 星期二

[Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (無參數方法)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 的繼承作了一些限制,這使你在使用繼承前必須多一份思考. 

無參數方法 : 
你定義了一個 Ball 類別 : 
  1. class Ball(r: Double) {  
  2.     def radius = r  
  3. }  
類別中,radius 方法不接受任何參數。在Scala中,不接受任何參數的方法,可以省略()的撰寫,這稱之為無參數方法Parameterless method),對於僅讀取而不改變物件狀態、且不接受任何參數的方法,在 Scala 中的慣例會省略括號,表示該方法不會有任何邊際效應Side effect),相對地,如果不接受任何參數的方法會產生邊際效應,則保留括號的撰寫,稱之為空括號方法(Empty-paren method), 今天假設有個客戶端使用了你的 Ball 類別 : 
  1. val ball = new Ball(10.0)  
  2. println(ball.radius)// 顯示 10.0  
同樣地,如果操作方法時,該方法如果不會產生邊際效應,則呼叫方法時慣例上也是省略括號。如果哪天你改變了 Ball 類別之撰寫 : 
  1. class Ball(val radius: Double)  
就剛才那個客戶端的程式碼並不會受到影響,這是 固定存取原則Uniform access principle) 的簡單例子。固定存取原則指的是,提供客戶端服務時所使用的名稱必須固定,無論該服務是透過計算或既有的結果. 就如同上例,客戶端要取得球的半徑,都是透過radius這個名稱來取得,無論是你將 radius 實作為方法或者是變數. 要注意的是在 Scala中,只有值(Value)與型態(Type)兩個名稱空間(Namespace),其中 : 
* 資料成員(Field)、方法(Method)、套件(Package)與單例(Singleton)物件屬於 (Value)名稱空間.
* 類別(Class)與特徵(Trait)屬於 型態(Type)名稱空間.
Ps. 由於單例物件與類別分屬於值與型態名稱空間,所以才可以相同的名稱形成伴侶.

由於方法與資料成員屬於同一個名稱空間,所以同一個類別中,資料成員不可以與方法是相同的名稱. 例如 : 
  1. class Ball {  // 這個類別會編譯錯誤  
  2.     private val radius = 10  
  3.     def radius = 20  
  4. }  
即使是繼承,如果子類別中有與父類別中的成員相同名稱的成員也不行。如果子類別中的方法名稱與父類別的方法名稱相同,則是重新定義方法,你必須使用 override 關鍵字指定。例如 : 
  1. class Ball(r: Double) {  
  2.     def volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3.     override def toString = "radius: " + r +  
  4.                             "\nvolume: " + volume  
  5. }  
  6.   
  7. class IronBall(r: Double) extends Ball(r)  
上例中定義了一個 IronBall,如果有客戶使用這個 IronBall 類別 : 
  1. val ball = new IronBall(10.0)  
  2. println(ball.volume)  
今天基於某個理由,你改寫了 IronBall : 
  1. class IronBall(r: Double) extends Ball(r) {  
  2.     override val volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3. }  
在 IronBall 中使用 val 定義了 volume 這個名稱,由於方法與資料成員屬於同一個名稱空間,如果你確定在子類別中要這麼作,必須重新定義,也就是使用 override 關鍵字指定,在 Scala 中,允許你將無參數方法重新定義為 val 資料成員。對於先前使用 IronBall 的客戶端而言,並沒有差別,這是固定存取原則的另一個簡單例子. 注意!你可以將父類別的無參數方法在子類別中重新定義為 val 資料成員,但不能將父類別的 val 資料成員在子類別中重新定義為無參數方法. 例如以下會編譯錯誤 : 
  1. class Ball(r: Double) {  
  2.     val volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3.     override def toString = "radius: " + r +  
  4.                             "\nvolume: " + volume  
  5. }  
  6.   
  7. class IronBall(r: Double) extends Ball(r) {  
  8.     // error, method volume is not stable  
  9.     override def volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  10. }  
不允許的理由在於,原先 volume 是個 val,也就是值固定不變,若能重新定義為方法,則方法的傳回值可能會因計算而不同,如果這個修改允許,你的客戶端在 IronBall 修改之後,執行的結果就會受到影響(如果 IronBall 的狀態是可變的話).

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...