ปี 2011 กูเกิลได้เปิดตัวภาษาโปรแกรมตัวใหม่ชื่อว่าภาษา Dart (เวอร์ชันแรก)
โครงสร้างของภาษา DART คล้ายกับ C/C++ และ Java โดยที่จะมีความเป็นภาษาแบบ Structure Programming แต่ก็ยังมีความสามารถแบบภาษาประเภท Object Oriented Programming ด้วย นั่นคือมี class
และ inheritance
ให้ใช้งาน
เป้าหมายของการสร้างภาษา Dart ขึ้นมา กูเกิลบอกว่าอยากสร้างภาษาเชิงโครงสร้างที่ยืดหยุ่นมากพอ (structured yet flexible language) และเป็นการออกแบบตัวภาษาไปพร้อมกับตัว Engine สำหรับรันภาษาเลยเพื่อแก้ปัญหาโปรแกรมทำงานช้าและกินmemory ซึ่งเป้าหมายของภาษา Dart คือเป็นภาษาที่เรียนรู้ง่าย และทำงานได้บนอุปกรณ์พกพาขนาดเล็ก มือถือ ไปจนถึงserver
ซึ่งสิ่งที่เด่นที่สุดสำหรับภาษา Dart ในตอนนี้คือเป็นภาษาที่ใช้ในการสร้าง Application ด้วยเฟรมเวิร์ก Flutter นั่นเอง!
ในตอนนี้ Dart มีออกมาแล้ว 2 เวอร์ชันคือ Dart1 และ Dart2, ในบทความนี้จะพูดถึง Dart2 เป็นหลัก
หากต้องการลองเล่นภาษา Dart ดู สามารถเข้าไปดาวน์โหลดตัว installer ได้ที่ dart.dev
หรือลองเขียนภาษา Dart แบบ online เลยได้ที่ DartPad
Hello World!
ตัวอย่างโปรแกรมของ Dart นั้นหน้าตาคล้ายๆ กับภาษา C มาก ถ้าใครเคยเขียนภาษา C หรือภาษาตระกูล C มาก่อน (เช่น C++, C#, Java) จะคุ้นกับ syntax พวกนี้ทำให้เรียนรู้ได้ไม่ยาก
Dart เป็นภาษากลุ่ม Compiler นั่นคือจำต้อง Compile ก่อนเอาโปรแกรมไปรัน ไม่เหมือนภาษากลุ่ม Script ที่ใช้ interpreter ในการรันตัว source code ตรงๆ
void main(){
print("Hello World!");
}
ตัวโปรแกรมจำเริ่มทำงานที่ฟังก์ชัน main
เป็นหลัก เราไม่สามารถเขียน statement นอกฟังก์ชันได้
การแสดงผลมาตราฐานจะใช้คำสั่ง print
(คำสั่งนี้ auto-newline เสมอนะ)
เรื่องหนึ่งที่ควรจำคือภาษา Dart นั้นการเขียน ;
(semi-colon) ไม่ใช่ optional คือจำเป็นต้องใส่ ; ทุกครั้งหลังจบ statement ไม่สามารถละ ;
ได้แบบภาษาตระกูล C ยุคใหม่ๆ เช่น JavaScript หรือ Kotlin
Comment
การใส่คอมเมนท์ทำได้เหมือนภาษา C ทุกอย่างคือ
-
//
สำหรับ inline comment - เปิดด้วย
/*
และปิดด้วย*/
สำหรับ multi-line comment (ไม่สามารถ nested ได้นะ)
int x; //ตั้งแต่ตรงนี้ไป เป็นส่วนของคอมเมนท์
/*
ในนี้
ทั้งหมด
เป็นคอมเมนท์
*/
Variable และ Data Type
type | คำอธิบาย | ตัวอย่าง |
---|---|---|
int |
เลขจำนวนเต็ม | 0, 1, -5, 86400 |
double |
เลขทศนิยม | 0.0, 0.1, 0.14, -12.34 |
num |
เลขทศนิยม หรือ เลขทศนิยม | 123, 0.123 |
bool |
ค่าทางตรรกศาสตร์ | true, false |
String |
สายอักขระ (ประโยค) | 'hello world!', "This is a book" <-- ในภาษา Dart สามารถใช้ได้ทั้ง " (double quote) และ ' (single quote) แต่เขาแนะนำให้ใช้ ' หรือ single quote กันนะ
|
dynamic |
ตัวแปรชนิดเปลี่ยนแปลงได้ | 1, 0.14, true, 'Hi!' |
ตัวแปรของ Dart ทั้งหมดเป็นแบบ reference type ทั้งหมด ทำให้สามารถมีค่าเป็น null
ได้ทั้งหมด
int x;
double d;
bool isDone;
String name;
//ตัวแปรทั้งหมดมีค่าเป็น null เพราะยังไม่ได้กำหนดค่า
แต่ใน Dart ยังมีชนิดของตัวแปรแบบพิเศษ ซึ่งไม่จำเป็นต้องประกาศ type เลย แต่ตัวภาษาจะ auto assign ชนิดของตัวแปรให้เอง
type | คำอธิบาย |
---|---|
var |
เป็นการละ type เอาไว้ให้โปรแกรมกำหนดให้ (ตาม value) |
final |
เหมือน var แต่ไม่สามารถเปลี่ยนแปลงค่าได้ |
const |
ค่าคงที่ |
ข้อแตกต่างระหว่าง dynamic
vs var
คือ
dynamic
เป็นการบอกว่าตัวแปรนี้ เก็บค่าชนิดไหนก็ได้ เปลี่ยนแปลงได้เรื่อยๆ (หากใครเคยเขียนภาษา script น่าจะคุ้นกัน) แน่นอนว่าการใช้ dynamic มีความเสี่ยงทำให้เกิด runtime error! ได้เพราะ Compiler ไม่สามารถช่วยเช็กชนิดของตัวแปรได้เลย
var
จะเป็นการกำหนดชนิดตัวแปรในจังหวะที่ประกาศตัวแปร โดยดูชนิดตัวแปรจาก value ตอนนั้นเลย หลังจากนั้นตัวแปรจะถูกกำหนดเป็น type นั้นไปตลอด ไม่สามารถเปลี่ยนแปลงได้แล้ว
dynamic d = 1; // ตอนนี้ค่า d เก็บค่า int
d = 'new value!'; // Ok! ค่า d เปลี่ยนไปเก็บค่า String แทน
d = true; // Ok! ค่า d เปลี่ยนไปเก็บค่า bool แทน
var v = 1; // สร้างตัวแปร v ซึ่ง value ในด้านขวาเป็น int ดังนั้นจะมีผลเท่ากับการเขียนว่า int v = 1; นั่นเอง
v = 'new value'; // Error: A value of type 'String' can't be assigned to a variable of type 'int'
ข้อแตกต่างระหว่าง final
vs const
คือ
final
เป็นการกำหนดว่าตัวแปรนี้ ไม่สามารถเปลี่ยนแปลงค่าได้ กำหนดค่าแล้วกำหนดเลย (immutable) ซึ่งเป็นตัวแปรประเภท runtime ดังนั้นเราสามารถกำหนดค่า final จากตัวแปรหรือฟังก์ชันอื่นได้
const
เป็นการประกาศค่าคงที่ โดยค่าที่กำหนดให้จะต้องเป็น literal เท่านั้น (เช่น 10, 'value') เพราะเป็นตัวแปรที่กำหนดค่าตั้งแต่ตอน compile-time
int x = 10;
final int f1 = 1; //กำหนดตัวแปร int ให้เป็นค่าคงที่
final f2 = 'final-val'; //ใช้เหมือน var คือไม่กำหนด type ก็ได้
final f3 = x + 20; //กำหนดค่าจากกโดยคำนวณมาจากตัวแปรอื่นอีกที
const int c1 = 1; //กำหนดตัวแปร int ให้เป็นค่าคงที่
const c2 = 'const-val'; //ใช้เหมือน var คือไม่กำหนด type ก็ได้
const c3 = x + 20; //Error: Not a constant expression. เพราะ x เป็นตัวแปรที่ value มาตอน runtime ไม่สามารถกำหนดให้ const ได้
Math Operation
การใช้ +
, -
, *
, /
และ %
เหมือนกับภาษาอื่นๆ แต่มีข้อควรระวังที่ตัว /
สำหรับภาษาอื่นถ้าเรานำ int / int
ผลที่ออกมาจะได้เป็น int
แน่นอน แต่สำหรับ Dart นั้นการหารจะได้ค่าออกมาเป็น double
เสมอ
int x = 4 / 2; // Error: A value of type 'double' can't be assigned to a variable of type 'int'.
int y = (int)(4 / 2); // Error: case แบบภาษา C ไม่ได้ด้วยนะ
วิธีการแก้คือใช้ operation ~/
คือการหารแล้วปัดจุดทิ้ง หรือใช้คำสั่ง as
หรือจะใช้คำสั่ง toInt()
ก็ได้
int x = 4 ~/ 2; // Ok! แบบนี้ได้
int x = 4 / 2 as int; // Ok! แบบนี้ได้
int x = (4 / 2).toInt(); // Ok! แบบนี้ได้
คำเตือน! ระวังสับสนกับคำสั่ง int.parse()
กับ int.tryParse()
ที่ใช้แปลง String --> int, เราไม่สามรถใช้ 2 คำสั่งนี้ในการแปลง double --> int ได้นะ
String Concatenate การต่อสตริง
การต่อสตริงใช้เครื่องหมาย +
เหมือนภาษาทั่วๆ ไป แต่ก็มีข้อควรระวัง (อีกแล้ว!) คือไม่สามารถต่อสตริงกับตัวแปรที่ไม่ใช่สตริงได้!
int x = 100;
print('x is ' + x); // Error: A value of type 'int' can't be assigned to a variable of type 'String'.
เราจะต้องแปลงตัวแปรที่ต้องการจะต่อสตริงให้เป็น String ซะก่อน หรือทำ String Interpolation ซะก่อน โดยใช้ตัว $
เพื่อระบุว่าตรงนี้เป็นตัวแปร (ถ้ามี expression ด้วยให้ครอบด้วย ${}
)
int x = 100, y = 200;
print('x is ' + x.toString()); // x is 100
print('x is $x'); // x is 100
print('x is ${x}'); // x is 100
print('x + y is ${x + y}'); // x + y is 300
Null Handling
ตัวแปรใน Dart เป็นแบบ reference ดังนั้นเลยสามารถเป็นค่า null
ได้ทุกตัวเลย ภาษาDartเลยมี operation สำหรับจัดการค่า null
พวกนี้มาให้เราใช้งานด้วย
??
Null Coalescing
เป็นการเช็กว่าตัวแปรตัวนี้ ถ้ามีค่าเป็น null
ให้ใช้ค่า default ที่กำหนดให้แทน
output = input ?? defaultValue;
// เป็น short-hand ของ...
if (input != null) {
output = input;
} else {
output = defaultValue;
}
เช่น
int number = ...;
int x = number ?? 1; // กำหนด x = number แต่ถ้า number เป็น null ให้กำหนด x = 1 แทน
?.
Null Conditional
หากตัวแปรของเราเป็น object ซึ่งสามารถเรียกใช้งาน method ต่างๆ ได้ ... แต่ถ้า object ตัวนั้นเป็น null
ก็จะเกิดปัญหา Null Pointer Exception ได้
object?.action();
// เป็น short-hand ของ...
if (object != null) {
object.action();
}
เช่น
class People{
void sayHi(){ print("hi!"); }
}
void main(){
People people = ...;
people?.sayHi(); // ถ้า people เป็น object ก็จะมีการปริ้นค่า "hi!" ออกมา แต่ถ้า people เป็น null คำสั่งนั้นก็จะไม่ถูกสั่งให้ทำงานเลย
}
??=
Null Coalescing Assignment
หากไม่ชัวร์ว่าตัวแปรตัวนั้นเป็น null
รึเปล่า สามารถใช้ ??=
กำหนดค่า default ลงไปได้
variable ??= defaultValue
// เป็น short-hand ของ...
variable = variable ?? defaultValue;
// หรือใช้ Ternary Operator
variable = variable != null ? variable : defaultValue;
// หรือเขียนแบบ if-else
if (input == null) {
output = defaultValue;
}
Flow Control
if-else
if (condition) {
// TODO
} else {
// TODO
}
switch case
switch (command) {
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
default:
executeUnknown();
}
ข้อควรระวัง! switch ในภาษา Dart ต้องมี break
ตอนจบ case ทุกครั้ง ถ้าไม่ใส่ลงไป โปรแกรมจะไม่หยุดทำงาน แล้วรันคำสั่งบรรทัดต่อไปต่อเลย
Loop: while, do-while
while (!isDone()) {
doSomething();
}
do {
printLine();
} while (!atEndOfPage());
เมื่อภาษาทั่วๆ ไป มีตัวcontrolเสริมคือ break
และ continue
ให้ใช้งานด้วย
Loop: for
for (var i = 0; i < 5; i++) {
print(i);
}
// output: 0 1 2 3 4
หรือใช้งานแบบ for-each สำหรับวนลูปทุก element ใน list
var numbers = [0, 1, 2, 3, 4];
for (var number in numbers) {
print(number);
}
// output: 0 1 2 3 4
Function
การสร้างฟังก์ชันในภาษา Dart มี syntax เหมือนภาษา C แต่สามารถละ type ทิ้งไปได้
เช่น
int add(int x, int y) {
return x + y;
}
// สามารถเขียนย่อได้ว่า
add(x, y) {
return x + y;
}
Arrow Function
และหากเคยเขียนภาษา JavaScript มา มีหลายครั้งที่เราสร้างฟังก์ชันที่มี return statement เดียวเท่านั้น เราก็สามารถเขียนย่อโดยใช้ Arrow Function ได้ ... และแน่นอน Dart ก็ทำได้เหมือนกัน โดยใช้ =>
int add(int x, int y) {
return x + y;
}
// สามารถเขียนย่อได้ว่า
add(x, y) => x + y;
Optional Parameter
เราสามารถกำหนดค่าเริ่มต้นให้ parameter ได้โดยใช้ []
ครอบ parameter ที่อยากประกาศให้เป็น optional
int add(int x, [int y = 1]) {
return x + y;
}
add(10, 20); // result: 30
add(10); // ไม่เซ็ตค่า y, ดังนั้น y = 1 result: 11
Named Parameter
บางกรณี การสร้างฟังก์ชันที่มี parameter เยอะมาก ตอนที่เรียกใช้ฟังก์ชันอาจจะงงเรื่องลำดับตัวแปรได้
int setConfig(
String basePath,
String appPath,
int retry,
int maxThread,
String defaultController
) {
// TODO
}
setConfig("/", "/app", 10, 4, "Main");
ในกรณีนี้เราสามาร้ถตั้งชื่อ parameter แต่ละตัวได้ โดยใช้ {}
int setConfig({
String basePath,
String appPath,
int retry,
int maxThread,
String defaultController
}) {
// TODO
}
setConfig(
basePath: "/",
appPath: "/app",
retry: 10,
maxThread: 4,
defaultController: "Main"
);
ซึ่งตัว parameter ทั้งหมด สามารถสลับตำแหน่งกันได้
ข้อควรระวัง! ตอนประกาศฟังก์ชันต้องมี {}
ครอบตัวแปร แต่ตอนเรียกใช้งานฟังก์ชัน ห้ามใส่ {}
ลงไปนะ
การใช้งาน Named Parameter จะถือว่าเป็น optional ทั้งหมด (แปลว่าไม่ใส่ค่าก็ได้) ซึ่งก็จะได้ค่าเป็น null
หากต้องการให้เวลาเรียกใช้งานฟังก์ชัน จำเป็นต้องใส่ค่านั้นลงไปเสมอจะต้องใช้ annotation @required
เข้ามาช่วย
ซึ่ง @required
นั้นอยู่ใน package ชื่อ meta
ที่ต้องติดตั้งเพื่อก่อนจะใช้งาน โดยการเพิ่ม dependency ในไฟล์ pubspec.yaml
dependencies:
meta: ^1.1.8
เวลาใช้งานก็...
import 'package:meta/meta.dart';
int setConfig({
@required String basePath,
@required String appPath,
int retry,
int maxThread,
String defaultController
}) {
// TODO
}
แบบนี้หมายความว่า parameter basePath
และ appPath
นั้นจำเป็นต้องใส่ทุกครั้งที่เรียกใช้งานฟังก์ชัน
First Class Function
ตามสไตล์ภาษาสมัยใหม่ เราสามารถจับฟังก์ชันใส่ตัวแปรได้
int getNumber() => 123;
void main(){
var func = getNumber; // ไม่ใช่ getNumber() นะ,ไม่มี ()
print(func()); // output: 123
}
หรือเราจะกำหนดว่าตัวแปรฟังก์ชันจะเป็น type อะไรและมี parameter อะไรบ้างก็ได้
โดยใช้รูปแบบการกำหนด type ดังนี้
return-type
Function
(params-type)
void func1(){ ... }
int func2(){ ... }
String func3(int x){ ... }
void main(){
void Function() f1 = func1;
int Function() f2 = func2;
String Function(int) f3 = func3;
}
และยังใช้ได้กับ method อีกด้วย เช่น
class People{
String sayHi() => "Hi!";
}
void main(){
People p = People();
String Function() f = p.sayHi;
print(f()); // output: Hi!
}
สรุป
บทความนี้ได้แนะนำภาษา Dart ซึ่งเราจะเห็นว่าภาษานี้นั้นให้อารมณ์เหมือนภาษาตระกูล C ที่มีการปรับอะไรให้เป็นภาษาสมัยใหม่มากขึ้น แต่ก็ยังไม่ทิ้งความเป็น Structure Language อยู่
หากใครเขียน C หรือหรือแม้แต่ภาษา(ที่เคย)โมเดิร์นอย่าง Java มาก่อน จะพบว่ามันมีความคล่องตัวในการเขียนมากขึ้น แต่ถ้าเอาไปเทียบกับภาษายุคใหม่อย่างเช่น Kotlin, Swift ก็ยังเรียกว่าสู้ไม่ได้ อาจจะมีอะไรขัดใจอยู่หลายอย่างเวลาเขียน
สรุปคือเป็นภาษาที่ค่อนข้างธรรมดา ไม่ได้ Wow! อะไรมาก แต่เราต้องใช้มัน หากต้องการจะต่อยอดไปเขียน Flutter ต่อไป
ในบทต่อไปเราจะมาดู Data Structure ในภาษา Dart กันต่อ
Top comments (4)
เห็นว่าการหารมีทั้ง / และ ~/ แบบเดียวกับ python เลยกังวลเรื่อง precision ยังไงรบกวนลองรันโค้ดนี้ดูหน่อยครับว่าให้คำตอบเหมือนกันมั้ย?
int x = pow(2, 63);
int ans1 = x ~/ 3;
int ans2 = x / 3 as int;
as
ทำงานก่อน/
ครับ ต้องใส่()
ด้วย ตามตัวอย่างในบทความเลยครับnote: หากต้องการลองรันโค้ด Dart สามารถเข้าไปลองรันได้ด้วยตัวเองที่ dartpad.dartlang.org/ เลยครับ
ไปลองในเว็บออนไลน์ละครับ กลายเป็นว่าได้คำตอบเป็น 0 ... เพราะว่ามันแปลง dart ไปเป็น js แล้วค่อยรันโค้ดผ่านเว็บเลย (ไม่ได้ส่งไปรันฝั่งเซิฟเวอร์) ทีนี้ js ไม่มีตัวเลขเป็น int อยู่แล้ว มีแต่ double 64 bit ถ้าเอามาเก็บ int คือเก็บได้ 53 bit แต่ dart ยอมให้เก็บ int แค่ 32 bit ดังนั้นไม่ว่ายังไง ~/ กับ / แล้วค่อยแปลงเป็น int ก็เลยได้คำตอบไม่ต่างกันอยู่แล้ว
ทีนี้ถ้าเอาโค้ดด้านบนไปลองบน dart ในเครื่องที่เป็น 64 bit (ผมใช้ 2.7.0 บน ubuntu) กลายเป็นว่าใช้ as int ไม่ได้เลยครับ ถูกด่ากลับมาแบบนี้
type 'double' is not a subtype of type 'int' in type cast
ส่วนโค้ดนี้ก็ทำให้เห็นชัดว่า ~/ ไม่เท่ากับ / แล้วแปลงทีหลังด้วย .toInt ครับ
int x = 1 << 62;
int y = x % 3;
int ans1 = x ~/ 3;
int rvt1 = 3*ans1 + y;
int ans2 = (x / 3).toInt();
int rvt2 = 3*ans2 + y;
print('x: $x');
print('');
print('ans1: $ans1');
print('rvt1: $rvt1');
print('same: ${x == rvt1}');
print('');
print('ans2: $ans2');
print('rvt2: $rvt2');
print('same: ${x == rvt2}');
ขอบคุณครับ เคลียร์ครับ