diff --git a/_th/tour/basics.md b/_th/tour/basics.md index d127d94613..6b1e99cc73 100644 --- a/_th/tour/basics.md +++ b/_th/tour/basics.md @@ -1,6 +1,6 @@ --- layout: tour -title: Basics +title: พื้นฐาน discourse: false @@ -13,4 +13,309 @@ next-page: unified-types previous-page: tour-of-scala --- +ในหน้านี้ เราจะครอบคลุมพื้นฐานของ Scala +In this page, we will cover basics of Scala. + +## ทดลอง Scala ในเว็บบราวเซอร์ + +เราสามารถรัน Scala ในเว็บเบราว์เซอร์ด้วย ScalaFiddle + +1. ไปที่ [https://scalafiddle.io](https://scalafiddle.io). +2. วาง `println("Hello, world!")` ในด้านซ้าย. +3. กดที่ปุ่ม "Run" . output จะแสดงในด้านขวา + +ในขั้นตอนนี้ง่ายมาก ที่จะได้ประสบการณ์ของเรากับ Scala + +หลายๆ โค้ดตัวอย่างในเอกสารนี้จะฝังใน ScalaFiddle ซึ่งคุณสามารถกดที่ปุ่ม Run เพื่อดูว่าโด้นนั้นๆ จะได้ผลลัพธ์อย่างไร + +## Expressions + +Expression หรือ นิพจน์ เป็นโค้ดที่ทำการคำนวนได้ +``` +1 + 1 +``` +เราสามารถแสดงผลลัพธ์ของ Expression ด้วยการใช้ `println` + +{% scalafiddle %} +```tut +println(1) // 1 +println(1 + 1) // 2 +println("Hello!") // Hello! +println("Hello," + " world!") // Hello, world! +``` +{% endscalafiddle %} + +### Values + +เราสามารถตั้งชื่อของผลลัพธ์ของ expression ด้วย keyword `val` + +```tut +val x = 1 + 1 +println(x) // 2 +``` + +ตั้งชื่อผลลัพธ์ อย่างเช่น `x` จะเรียก value การอ้างอิง value จะไม่มีการคำนวณอีกครั้ง + +Value ไม่สามารถกำหนดค่าใหม่ได้ + +```tut:fail +x = 3 // ตรงนี้ไม่ compile. +``` + +type (ชนิด) ของ value สามารถ inferred (อนุมาน) ได้ แต่เราสามารถกำหนดชนิดของ type อย่างชัดเจนได้ แบบนี้ + +```tut +val x: Int = 1 + 1 +``` + +สังเกตว่า การประกาศชนิดของ type `Int` จะระบุหลังจาก indentifier `x` เราจำเป็นต้องมี `:` + +### Variables + +ตัวแปรเหมือนกับ value ยกเว้นแต่ว่าเราสามารถกำหนดค่าใหม่ได้ เราสามารถกำหนดตัวแปรด้วย keyword `var` + +```tut +var x = 1 + 1 +x = 3 // ตรงนี้ compile เพราะว่า "x" ถูกประกาศด้วย keyword "var" +println(x * x) // 9 +``` + +เราสามารถกำหนด type ได้ตามที่เราต้องการ: + +```tut +var x: Int = 1 + 1 +``` + + +## Blocks + +เราสามารถรวมหลายๆ expression ไว้ด้วยกันด้วยการครอบด้วย `{}` เรียกมันว่า block + +ผลลัพธ์ของ expression สุดท้ายใน block จะเป็นผลลัพธ์ของ block ทั้งหมดด้วย + +```tut +println({ + val x = 1 + 1 + x + 1 +}) // 3 +``` + ## Functions + +function เป็น expression ที่รับ parameter ได้ + +เราสามารถกำหนด anonymous function (เป็น function ที่ไม่มีชื่อ) ที่ return ค่าตัวเลขบวกหนึ่ง: + +```tut +(x: Int) => x + 1 +``` + +ในด้านซ้ายของ `=>` คือรายการของ parameter ในด้านขวาเป็น expression ที่นำ parameter มาใช้ + +เราสามารถตั้งชื่อของ function ได้ดังนี้ + +{% scalafiddle %} +```tut +val addOne = (x: Int) => x + 1 +println(addOne(1)) // 2 +``` +{% endscalafiddle %} + +function สามารถรับ parameter ได้หลายตัว + +{% scalafiddle %} +```tut +val add = (x: Int, y: Int) => x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +หรือ เราจะไม่รับ parameter เลยก็ได้ + +```tut +val getTheAnswer = () => 42 +println(getTheAnswer()) // 42 +``` + +## Methods + +Method มีลักษณะเหมือนกับ function มาก แต่ว่าจะมีบางสิ่งที่แตกต่างกันระหว่าง method และ function + +Method จะประกาศได้ด้วย keyword `def` ตามด้วยชื่อของ function, รายการ parameter, return type และ body ของ function + +{% scalafiddle %} +```tut +def add(x: Int, y: Int): Int = x + y +println(add(1, 2)) // 3 +``` +{% endscalafiddle %} + +สังเกตว่า การ return type จะประกาศ _หลังจาก_ รายการ parameter และ colon `: Int` + +Method ยังสามารถรับรายการ parameter ได้หลายรายการ + +{% scalafiddle %} +```tut +def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier +println(addThenMultiply(1, 2)(3)) // 9 +``` +{% endscalafiddle %} + +หรือ ไม่มีรายการ parameter เลยก็ได้ อย่างเช่น + +```tut +def name: String = System.getProperty("user.name") +println("Hello, " + name + "!") +``` + +และยังมีบางสิ่งที่แตกต่างกัน แต่ตอนนี้เราจะคิดว่า method มีความเหมือนกับ function + +Method สามารถมี expression ได้หลายบรรทัด +```tut +def getSquareString(input: Double): String = { + val square = input * input + square.toString +} +``` +expression สุดท้ายใน body เป็น expression ที่ return value ของ method (Scala ก็มี keyword `return` แต่ว่าไม่ค่อยได้ใช้) + +## Classes + +เราสามารถประกาศ class ได้ด้วย keyword `class` ตามด้วยชื่อของ class และ constructor parameters + +```tut +class Greeter(prefix: String, suffix: String) { + def greet(name: String): Unit = + println(prefix + name + suffix) +} +``` +return type ของ method `greet` เป็น `Unit` ซึ่งอาจจะกล่าวได้ว่าไม่มีการ return มันใช้เหมือน `void` ใน Java และ C (ความแตกต่างคือทุกๆ expression ของ Scala จำเป็นต้องมีค่า ซึ่งเป็น singleton vlaue จริงๆ ของ Unit เขียนด้วย () ซึ่งไม่มีข้อมูลใดๆ) + +เราสามารถสร้าง instance ของ class ได้ด้วย keyword `new` + +```tut +val greeter = new Greeter("Hello, ", "!") +greeter.greet("Scala developer") // Hello, Scala developer! +``` + +เราจะครอบคลุมเรื่องของ class ในเชิงลึก [ภายหลัง](classes.html) + +## Case Classes + +Scala มี type ชนิดพิเศษของ class เรียกว่า "case" class โดยเริ่มต้นแล้ว case class เป็นค่าที่เปลี่ยนแปลงไม่ได้ (immutable) และสามารถเปลียบเทียบด้วย value เราสามารถประกาศ case class ด้วย keyword `case class` + +```tut +case class Point(x: Int, y: Int) +``` + +เราสามารถสร้าง instant ของ case class โดยไม่ต้องใช้ keyword `new` + +```tut +val point = Point(1, 2) +val anotherPoint = Point(1, 2) +val yetAnotherPoint = Point(2, 2) +``` + +และสามารถเปรียบเทียบค่าของ case class ได้ + +```tut +if (point == anotherPoint) { + println(point + " and " + anotherPoint + " are the same.") +} else { + println(point + " and " + anotherPoint + " are different.") +} // Point(1,2) and Point(1,2) are the same. + +if (point == yetAnotherPoint) { + println(point + " and " + yetAnotherPoint + " are the same.") +} else { + println(point + " and " + yetAnotherPoint + " are different.") +} // Point(1,2) and Point(2,2) are different. +``` + +เป็นตัวอย่างการใช้งานของ case class ที่เราอยากจะแนะนำ และอยากให้คุณตกหลุมรักมัน เราจะครอบคลุมในเชิงชึกใน [ภายหลัง](case-classes.html) + +## Objects + +Object เป็น instance เดี่ยวของ definition ของมัน เราสามารถคิดว่ามันเป็น singleton ของ class ที่มันเป็นเจ้าของ + +เราสามารถประกาศ object ได้ด้วย keyword `object` + +```tut +object IdFactory { + private var counter = 0 + def create(): Int = { + counter += 1 + counter + } +} +``` + +เราสามารถเข้าถึง object ด้วยการอ้างอิงถึงชื่อของมัน + +```tut +val newId: Int = IdFactory.create() +println(newId) // 1 +val newerId: Int = IdFactory.create() +println(newerId) // 2 +``` + +เราจะครอบคลุมในเชิงลึกใน [ภายหลัง](singleton-objects.html) + +## Traits + +Trait เป็น type ที่บรรจุ field และ method ที่แน่นอน เราสามารถรวม trait หลายๆ trait เข้าด้วยกันได้ + +เราสามารถประกาศ trait ได้ด้วย keyword `trait` + +```tut +trait Greeter { + def greet(name: String): Unit +} +``` + +Trait สามารถมี default implementation ได้ + +{% scalafiddle %} +```tut +trait Greeter { + def greet(name: String): Unit = + println("Hello, " + name + "!") +} +``` + +เราสามารถขยาย traint ได้ด้วย keyword `extents` และ overrid implementation ด้วย keyword `override` + +```tut +class DefaultGreeter extends Greeter + +class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { + override def greet(name: String): Unit = { + println(prefix + name + postfix) + } +} + +val greeter = new DefaultGreeter() +greeter.greet("Scala developer") // Hello, Scala developer! + +val customGreeter = new CustomizableGreeter("How are you, ", "?") +customGreeter.greet("Scala developer") // How are you, Scala developer? +``` +{% endscalafiddle %} + +จากตัวอย่างนี้ `defaultGreeter` ขยายเพียง trait เดียว แต่มันสามารถขยายหลาย trait + +เราจะครอบคลุมในเชิงลึกใน [ภายหลัง](traits.html) + +## Main Method + +main method เป็น entry point หรือจุดเริ่มต้นของโปรแกรม ใน ​Java Virtual Machine +ต้องการ main method ชื่อว่า `main` และสามารถรับ argument ที่เป็น array ของ string + +ใช้ object เราสามารถประกาศ main method ได้ดังนี้: + +```tut +object Main { + def main(args: Array[String]): Unit = + println("Hello, Scala developer!") +} +``` \ No newline at end of file diff --git a/_th/tour/classes.md b/_th/tour/classes.md index f77415a2e9..ec3ea62e67 100644 --- a/_th/tour/classes.md +++ b/_th/tour/classes.md @@ -1,6 +1,6 @@ --- layout: tour -title: Classes +title: คลาส discourse: false @@ -13,3 +13,109 @@ language: th next-page: traits previous-page: unified-types --- + +คลาสใน Scala เป็นพิมพ์เขียวสำหรับสร้าง object ในคลาสสามารถมี method, value, ตัวแปร, type, object, +trait และคลาส ซึ่งเรียกรวมๆ กันว่า _members_ หรือ _สมาชิก_ ของคลาส type, object และ trait จะกล่าวถึงภายหลัง + +## การกำหนดคลาส +วิธีการที่ง่ายที่สุดในการกำหนดคลาสด้วยการใช้ keyword `class` และ +identifier ชื่อของคลาสควรจะขึ้นต้นด้วยตัวพิมพ์ใหญ่ +```tut +class User + +val user1 = new User +``` +keyword `new` ใช้เพื่อสร้าง instance ของคลาส `User` มี constructor เริ่มต้นซึ่งไม่รับค่า argument เพราะว่าไม่ได้กำหนด constructor ไว้ตอนประกาสคลาส + +อย่างไรก็ตาม, เราอาจจะมี constructor และ body ของคลาส +ตัวอย่างดังนี้ เป็นการประกาศคลาสสำหรับจุด (point): + +```tut +class Point(var x: Int, var y: Int) { + + def move(dx: Int, dy: Int): Unit = { + x = x + dx + y = y + dy + } + + override def toString: String = + s"($x, $y)" +} + +val point1 = new Point(2, 3) +point1.x // 2 +println(point1) // พิมพ์ (2, 3) +``` + +คลาส `Point` นี้มีสมาชิก 4 ตัว คือ ตัวแปร `x` และ `y` และ method `move` และ `toString` +ไม่เหมือนภาษาอื่นๆ, ซึ่ง constructor หลักจะอยู่ใน class signature `(var x: Int, var y: Int)` +method `move` รับ argument ชนิดตัวเลข 2 ตัว และ return เป็นค่า Unit `()` ซึ่งไม่มีค่า +จะมีผลลัพธ์คลายกับ `void` ในภาษาที่เหมือน Java, `toString` ในทางกลับกัน ไม่รับ argument ใดๆ แต่ return เป็นค่า `String` ซึ่งแทนที่ method `toString` จาก [`AnyRef`](unified-types.html) โดยจะมี keyword `override` + +## Constructors + +​Constructor สามารถมี parameter ตัวเลือกได้ โดยกำหนดค่าเริ่มต้นดังนี้: + +```tut +class Point(var x: Int = 0, var y: Int = 0) + +val origin = new Point // x and y are both set to 0 +val point1 = new Point(1) +println(point1.x) // พิมพ์ 1 + +``` + +ในคลาส `Point` ดังกล่าว `x` และ `y` มีค่าเริ่มต้นเป็น `0` ดังนั้นเราสามาถไม่ใส่ argument ก็ได้ +อย่างไรก็ตาม เพราะว่า constructor อ่าน argument จากซ้ายไปขวา ถ้าเราต้องการใส่ค่าใน `y` ไปในคลาส +เราจำเป็นต้องระบุชื่อของ parameter ด้วย +``` +class Point(var x: Int = 0, var y: Int = 0) +val point2 = new Point(y=2) +println(point2.y) // พิมพ์ 2 +``` + +นี้เป็นวิธีปฏิบัติที่ดีเพื่อจะทำให้โค้ดชัดเจนมากขึ้น + +## Private Members และ Getter/Setter +สมาชิกของคลาสจะเป็น public โดยค่าเริ่มต้น ใช้ access modifier `private` +เพื่อซ้อนสมาชิกนั้นจากภายนอกของคลาส +```tut +class Point { + private var _x = 0 + private var _y = 0 + private val bound = 100 + + def x = _x + def x_= (newValue: Int): Unit = { + if (newValue < bound) _x = newValue else printWarning + } + + def y = _y + def y_= (newValue: Int): Unit = { + if (newValue < bound) _y = newValue else printWarning + } + + private def printWarning = println("WARNING: Out of bounds") +} + +val point1 = new Point +point1.x = 99 +point1.y = 101 // พิมพ์คำเตือน warning +``` +คลาส `Point` เวอร์ชันนี้ ข้อมูลจะถูกเก็บไว้ในตัวแปรชนิด private ที่ชื่อว่า `_x` และ `_y` และมี method ที่ชื่อว่า `def x` และ `def y` ทีจะใช้ในการเข้าถึงข้อมูล private เป็น getter, `def x_=` และ `def y=` +เป็น method สำหรับตรวจสอบข้อมูลและ setting ค่าของตัวแปร `_x` และ `_y` +สังเกตว่า syntax พิเศษนี้สำหรับ setter: คือ method ที่ตามด้วย `_=` ไปยังตัวระบุของ setter และ parameter ตามหลังมา + +constructor หลักกำหนด parameter ด้วย `val` และ `var` เป็น public อย่างไรก็ตามเพราะว่า `val` เป็นตัวแปรที่เปลี่ยนแปลงไม่ได้ (immutable) เราไม่สามารถเขียบแบบนี้ได้ +``` +class Point(val x: Int, val y: Int) +val point = new Point(1, 2) +point.x = 3 // <-- ตรงนี้ไม่ compile +``` + +parameter ที่ไม่มี `val` หรือ `var` เป็นค่า private จะมองเห็นได้เพียงข้างในคลาส +``` +class Point(x: Int, y: Int) +val point = new Point(1, 2) +point.x // <-- ตรงนี้ไม่ compile +``` \ No newline at end of file diff --git a/_th/tour/traits.md b/_th/tour/traits.md index 87f1277df6..63ec0e4f63 100644 --- a/_th/tour/traits.md +++ b/_th/tour/traits.md @@ -13,3 +13,72 @@ language: th next-page: mixin-class-composition previous-page: classes --- + +Trait ใช้เพื่อแชร์ interface และ field ระหว่างคลาส มันจะเหมือนกับ interface ใน Java 8 +คลาส และ object สามารถขยาย trait ได้แต่ trait ไม่สามารถ instant เป็น object และไม่สามารถมี parameter ได้ + +## การกำหนด trait +วิธีที่ง่ายที่สุดในการกำหนด trait คือการประกาศด้วย keyword `trait` และ indentifier: + +```tut +trait HairColor +``` +trait จะมีประโยชน์อย่างยิ่งด้วยการเป็น generic type และเป็น abstract method +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} +``` + +การขยาย `trait Iterator[A]` ต้องการ type `A` และ implementation ของ method `hasNext` และ `next` + +## การใช้ traits +ใช้ keyword `extends` เพื่อขยาย trait ดังนั้นจะ implement abstract member ใดๆ ของ trait โดยใช้ keyword `override`: +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} + +class IntIterator(to: Int) extends Iterator[Int] { + private var current = 0 + override def hasNext: Boolean = current < to + override def next(): Int = { + if (hasNext) { + val t = current + current += 1 + t + } else 0 + } +} + + +val iterator = new IntIterator(10) +iterator.next() // returns 0 +iterator.next() // returns 1 +``` +คลาส `IntIterator` นี้รับค่า parameter `to` เป็น upper bound มัน `extends Iterator[Int]` ซึ่งหมายความว่า method `next` จะต้อง return เป็น Int + +## Subtyping +ในเมื่อ trait ที่ให้มานั้น required, subtype ของ trait สามารถถูกใช้แทนที่ได้ +```tut +import scala.collection.mutable.ArrayBuffer + +trait Pet { + val name: String +} + +class Cat(val name: String) extends Pet +class Dog(val name: String) extends Pet + +val dog = new Dog("Harry") +val cat = new Cat("Sally") + +val animals = ArrayBuffer.empty[Pet] +animals.append(dog) +animals.append(cat) +animals.foreach(pet => println(pet.name)) // พิมพ์ Harry Sally +``` +`trait Pet` มี abstract field `name` ซึ่ง implement โดย Cat และ Dog ใน constructor ของมัน +ในบรรทัดสุดท้าย เราเรียก `pet.name` ซึ่งจะต้องถูก implement แล้วใน subtype ใดๆ ของ trait `Pet` \ No newline at end of file diff --git a/_th/tour/unified-types.md b/_th/tour/unified-types.md index 234932259c..dbb6bbeae7 100644 --- a/_th/tour/unified-types.md +++ b/_th/tour/unified-types.md @@ -1,6 +1,6 @@ --- layout: tour -title: Unified Types +title: ชนิดข้อมูล discourse: false @@ -13,3 +13,70 @@ language: th next-page: classes previous-page: basics --- + +ใน Scala, value ทั้งหมดมี type รวมทั้ง value ที่เป็นตัวเลขและ function ในแผนภาพด้านล่างแสดงให้เห็นโครงสร้างของ type ใน Scala + +Scala Type Hierarchy + +## โครงสร้างของ Type ใน Scala ## + +[`Any`](http://www.scala-lang.org/api/2.12.1/scala/Any.html) เป็น supertype ของ type ทั้งหมด และยังเรียกว่า type บนสุด มันจะกำหนด method ที่ใช้งานร่วมกันอย่างเช่น `equals`, `hashCode` และ `toString` ซึ่ง `Any` มี subclass โดยตรง 2 subclass คือ `AnyVal` และ `AnyRef` + +`AnyVal` แทน value type หรือชนิดข้อมูลที่มีค่า ซึ่งมี 9 value type และเป็นค่าที่ไม่สามารถเป็น null (non-nullable): `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit` และ `Boolean` ซึ่ง `Unit` เป็น value type ที่แทนค่าที่ไม่มีข้อมูล โดยที่มีหนึ่งค่า instance ของ `Unit` ซึ่งสามารถประกาศด้วย `()` ทุก function จำเป็นต้องมีการ return บางสิ่งบางอย่าง ซึ่งก็ `Unit` ก็เป็นค่าที่ใช้เป็น return type + +`AnyREf` แทน reference type หรือชนิดข้อมูลที่ใช้อ้างอิง ทั้งหมดของ type ที่ไม่มี value จะเป็น reference type ทุกๆ type ที่ผู้ใช้งานกำหนด (user-defined) ใน Scal เป็น subtype ของ `AnyRef` ใน Scala ถูกใช้ในบริบทของ Java runtime environment ซึ่ง `AnyRef` จะสอดคล้องกับ `java.lang.Object` ใน Java + +นี่เป็นตัวอย่างที่แสดงให้เห็นการใช้งาน string, integer, charecter, boolean value และ function เป็น object ทั้งหมดที่เหมือนกับ obejct อื่น: + +```tut +val list: List[Any] = List( + "a string", + 732, // an integer + 'c', // a character + true, // a boolean value + () => "an anonymous function returning a string" +) + +list.foreach(element => println(element)) +``` + +จะกำหนดตัวแปร `list` ของ type `List[Any]` ซึ่งเป็น list ที่สร้างขึ้นด้วยองค์ประกอบของหลายๆ type แต่ว่าทั้งหมดจะเป็น instance ของ `scala.Any` ดังนั้นเราสามารถเพิ่มมันเข้าไปใน list ได้ + +นี่เป็น output ของโปรแกรม: + +``` +a string +732 +c +true + +``` + +## การแปลง Type +Value type สามารถแปลได้ด้วยวิธีดังนี้: +Scala Type Hierarchy + +ตัวอย่างเช่น: + +```tut +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 (หมายเหตุว่าค่าความละเอียดจะสูญหายไปในกรณีนี้) + +val face: Char = '☺' +val number: Int = face // 9786 +``` + +การแปลงค่าทิศทางเดียว ซึ่งตังอย่างเหล่านี้จะไม่ compile และจะฟ้อง error: + +``` +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 +val z: Long = y // ไม่เป็นไปตามที่ต้องการ +``` + +เราสามารถแปลง reference type ไปเป็น subtype ได้ โดยจะครอบคลุมในภายหลัง + +## Nothing และ Null +`Nothing` เป็น subtype ของ value type ทั้งหมด และยังเรียกว่าเป็น type ล่างสุด ค่าที่ไม่มีค่าจะเป็น type `Nothing` ส่วนมากจะใช้ในกรณี single non-termination อย่างเช่น throw exception, program exit หรือ infinite loop (นั้นคือ มันเป็น type ของ expression ที่ไม่มีการประเมินค่าของ value หรือ method ที่ไม่มีการ return ค่าในแบบปรกติ) + +`Null` เป็น subtype ของ reference type ทั้งหมด (นั้นคือ เป็น subtype ของ AnyRef) มันมีการระบุ value เดียวด้วย keyword `null` ซึ่ง `Null` ใช้ส่วนใหญ่สำหรับการทำงานร่วมกันกับภาษา JVM และไม่ควรใช้ในโค้ดของ Scala เราจะครอบคลุมวิธีการอื่นแทน `null` ในภายหลัง \ No newline at end of file