ในการเรียกใช้ฟังก์ชั่นที่ผู้ใช้งานเขียนขึ้น จำเป็นต้องระบุตัวแปรที่จะส่งให้ในโปรแกรม แต่หากมีความจำเป็นต้องเขียนฟังก์ชั่นที่อาจรับค่าจากข้อมูลภายนอก ที่ไม่สามารถระบุใจขณะเขียนโปรแกรมได้ ตัว Dart มีคำสั่ง Function.apply()
ซึ่งเป็น static method ของ Function class มาช่วยในงานแบบนี้
Dart รองรับการส่งตัวแปรทั้งแบบตามลำดับตำแหน่ง(positional parameters) และแบบใช้ชื่อตัวแปร(named parameters) ดูรายละเอียดเพิ่มเติมได้ที่ Functions | Dart
num plusOne(num input) {
return input + 1;
}
num plusTwo({required input}) {
return input + 2;
}
num addThem(num input1, {required input2}) {
return input1 + input2;
}
void main() {
print(plusOne(1)); // output → 2
print(plusTwo(input: 1)); // output → 3
print(addThem(1, input2: 2)); // output → 3
}
จากตัวอย่างจะเห็นว่า การเรียกใช้ฟังก์ชั่น ต้องเขียนระบุลงไปเลยว่าจะเรียกใช้ฟังก์ชั่นโดยส่งผ่านค่าอะไรไปให้บ้าง
Function.apply()
สำหรับคนที่เคยเขียนมาหลายภาษาโปรแกรม การใช้คำสั่ง Function.apply()
คงเป็นเรื่องธรรมดา ถึงแม้จะไม่ได้ค่อยได้ใช้ก็ตาม รูปแบบวิธีการใช้งานมีดังนี้
external static apply(
Function function,
List<dynamic>? positionalArguments,
[Map<Symbol, dynamic>? namedArguments]
);
จากตัวอย่างแรก หากเขียนด้วย Function.apply()
สามารถทำได้ดังนี้
num plusOne(num input) {
return input + 1;
}
num plusTwo({required input}) {
return input + 2;
}
num addThem(num input1, {required input2}) {
return input1 + input2;
}
void main() {
print(Function.apply(plusOne, [1])); // output → 2
print(Function.apply(plusTwo, [], {#input: 1})); // output → 3
print(Function.apply(addThem, [1], {#input2: 2})); // output → 3
print(Function.apply(plusTwo, [], {Symbol('input'): 1})); // output → 3
print(Function.apply(addThem, [1], {Symbol('input2'): 2})); // output → 3
}
มาถึงตรงนี้ ก็อาจมีคนสงสัยว่า ในเมื่อฟังก์ชั่นใน Dart มันสามารถรับ List เป็น parameters ได้อยู่แล้ว จะมาเขียนแบบนี้ทำไม ถ้าตอบแบบกำปั้นทุบดิน ก็เพราะมันเข้าใจการทำงานง่ายกว่า เช่น หากเขียนฟังก์ชั่นเพื่อรับค่าที่เป็น optional มันสามารถเขียนลงไปตอนประกาศฟังก์ชั่นได้เลย
String info1(String name, [String nationality = 'Thai', int age = 0]) {
return "Name: $name\nNationality:$nationality\nAge:$age";
}
String info2(List<dynamic> info) {
if (info.isEmpty) {
throw Exception('require name');
}
return "Name: ${info[0]}\nNationality:${info.elementAtOrNull(1) ?? 'Thai'}\nAge:${info.elementAtOrNull(2) ?? 0}";
}
void main() {
print(Function.apply(info1, ['Somchai N.']));
print(info2(['Somchai N.']));
}
จากตัวอย่าง ทั้ง info1()
และ info2()
ต่างก็ทำงานเหมือนกัน แต่การประกาศค่า parameters นั้น การที่ระบุชื่อตัวแปร ประเภทตัวแปร รวมถึงค่า default ทำให้อ่านง่ายกว่าที่ส่งมาเป็น List ใน info2
การเรียกแบบ dynamic มันช่วยให้ลดการเขียนโปรแกรมเพื่อเอาค่าที่ได้จากข้อมูลภายนอก มาจัดรูปแบบเพื่อนำส่งให้ฟังก์ชั่นประมวลผลได้ เช่น หากมี Text file ที่เป็นจากตัววัดอุณหภูมิ ที่จะเก็บในรูปแบบต่อไปนี้
date time degree rh 2024-01-01 12:00 15.5 30 2024-01-01 13:00 15.5 45 2024-01-01 14:00 2024-01-01 15:00 14 60
โดยมันจะมีค่าว่างหากตัวข้อมูลวัดไม่ได้ ผู้เขียนก็เขียนฟังก์ชั่นเพื่อเอาข้อมูลดังกล่าวมาประมวลผลได้ดังนี้
import 'dart:io';
void getTempRh([String? date, String? time, String? degree, String? rh]) {
// process data
print("$date $time $degree $rh");
}
void main() {
// ** example: read data from file **
// File textData = File('path/to/datafile.txt');
// List<String> lines = textData.readAsLinesSync();
// ** example: use inline demo data **
String textData = """
2024-01-01 12:00 15.5 30
2024-01-01 13:00 15.5 45
2024-01-01 14:00
2024-01-01 15:00 14 60
""";
List<String> lines = textData.split("\n");
for (String line in lines) {
List<String> param = line.trim().split(' ');
Function.apply(getTempRh, param);
}
}
ผลที่ได้
2024-01-01 12:00 15.5 30
2024-01-01 13:00 15.5 45
2024-01-01 14:00 null null
2024-01-01 15:00 14 60
null null null
อีกตัวอย่าง หากได้ข้อมูลจากการอ่านค่าเป็น JSON ในงานที่อ่านข้อมูลมาจาก Server ก็สามารถส่งข้อมูลแบบ dynamic call ได้เช่นเดียวกัน
import "dart:convert";
void getTempRh({String? date, String? time, String? degree, String? rh}) {
// process data
print("$date $time $degree $rh");
}
void main() {
String jsonText = """[
{"date":"2024-01-01","time":"15:00","degree":"15.5","rh":"40"},
{"date":"2024-01-01","time":"16:00","degree":"15","rh":"45"}
]""";
var data = jsonDecode(jsonText);
for (Map line in data) {
Function.apply(getTempRh, [], line.map((k, v) => MapEntry(Symbol(k), v)));
}
}
ผลที่ได้
2024-01-01 15:00 15.5 40
2024-01-01 16:00 15 45
จากตัวอย่าง JSON เนื่องจากตัว named arguments ต้องส่งในรูปแบบ Map<Symbol, dynamic>
จึงจำเป็นต้องแปลงตัว key ที่เป็น String
ให้เป็น Symbol
เสียก่อน โดยเรียกใช้คำสั่ง .map()
เพื่อแปลงค่า key ของทุก elements
สามารถดูวิธีการใช้งาน Map เบื้องต้นได้ที่ note ตัวนี้
เนื่องจากมันเป็นการทำงานขณะ runtime ทำให้ไม่สามารถตรวจสอบได้ว่าตัวแปรที่ส่งให้ประเภทถูกต้องตามที่ต้องการหรือไม่ อาจทำให้เกิด Exception error ได้ และหากใช้งานไม่วางแผนให้ดีพอจะกลายเป็น bugs เพราะมีช่องโหว่ที่ตัว compiler ไม่สามารถช่วยตรวจสอบให้ได้ ดังนั้นถ้ามันเป็นงานที่ไม่ซับซ้อนมาก ผู้เขียนโปรแกรมสามารถควบคุมดูแลความถูกต้องของข้อมูลที่จะใช้ประมวลผลได้ ก็ถือว่าช่วยผ่อนแรงในการเขียนโปรแกรมไปได้เยอะพอสมควรกับการเรียกใช้คำสั่ง Function.apply()
โปรดใช้งานด้วยความระมัดระวัง