var data = newint[] { 1,2,3}; var sum = data.Aggregate(0, (acc,x) => acc + x); // sum = 6 var pro = data.Aggregate(1, (acc,x) => acc * x); // pro = 6
Fold
1 2 3 4
open System.Linq let data = [|1;2;3|] let sum = Array.fold (fun acc x -> acc + x) 0 data // sum = 6 let pro = Array.fold (fun acc x -> acc * x) 1 data // pro = 6
ฟังก์ชั่น fold รับ parameter ทั้งหมด 3 ตัว คือ
folder คือ ฟังก์ชันสำหรับ transform หรือ update state
state คือ initial value หรือค่าเริ่มต้นที่จะถูกใช้ในฟังก์ชัน folder
input คือ ข้อมูลที่ต้องการ process
Reduce
1 2 3 4 5 6 7 8
let data = [1;2;3] let empty = []
let sum1 = List.reduce(fun acc x -> acc + x) data // sum1 = 6 let sum2 = List.reduce(fun acc x -> acc + x) empty // ERROR: The input list was empty.
let sum3 = List.fold(fun acc x -> acc + x) 0 data // sum3 = 6 let sum4 = List.fold(fun acc x -> acc + x) 0 empty // sum4 = 0
ฟังก์ชั่น reduce จะคล้ายกับ fold ต่างกันที่เราไม่สามารถใส่ state หรือ initial value แบบ explicit ได้ fold จึงต้องดึง element ตัวแรกของ input เป็น initial value เสมอ ทำให้เกิดข้อจำกัด คือ ข้อมูล input จำเป็นต้องมีอย่างน้อย 1 element มิฉะนั้นจะเกิด error System.ArgumentException: The input list was empty
ความแตกต่างอีกข้อคือ initial value ที่ใสในฟังก์ชั่น fold สามารถเป็น type ใดก็ได้ ส่งผลให้ลัพท์สุดท้ายไม่จำเป็นต้องเป็น type เดียวกับ input element จะแตกต่างจาก reduce ที่ output ต้องเป็น type เดียวกับ input element เสมอ
Context ใน EF ใช้เป็น interface สำหรับ insertupdatedelete และ query ข้อมูล โดยการประกาศ context ต้อง inherit class ชื่อ DbContext
1 2 3 4 5 6 7
typeAppContext(connection:string) = inherit DbContext(connection) [<DefaultValue>] valmutableprivate users: DbSet<QUser> member this.Users with get() = this.users and set v = this.users <- v
เมื่อเขียน Entity class และ context ครบแล้ว ต่อไปคือการ generate ฐานข้อมูลให้ได้ table และ field ตรง ที่ตามที่ประกาศไว้ใน Entity class วิธีการคือ ให้เพิ่มโค้ด Database.SetInitializer ก่อนที่จะทำ operation ใด ๆ ก็ตามกับฐานข้อมูล
1 2 3 4
Database.SetInitializer<AppContext> (new DropCreateDatabaseIfModelChanges<AppContext>()); use context = new AppContext("production") let rs = context.Users.Where(fun x -> x.FirstName = "").FirstOrDefault()
จากตัวอย่างใช้วิธีส่งคำสั่ง new DropCreateDatabaseIfModelChanges<AppContext>() ซึ่งหมายความว่าให้ drop ฐานข้อมูลและสร้างใหม่ถ้าตรวจเจอว่า Entity class มีการแก้ไข
User-define conversion - เขียน method สำหรับแปลง type ขึ้นเอง
Helper class - ใช้ helper ที่มีอยู่แล้ว เช่น คลาส Convert
Explicit conversion
ใช้ cast
1 2 3 4
object x = ...; if (x is String) { var str = (String) x; }
Cast มักใช้คู่กับ is เพื่อทำ type checking สำหรับกัน error InvalidCastException ในกรณีที่ CLR ไม่สามารถแปลง object ให้เป็น type ที่ต้องการได้
ใช้ as
1 2 3 4
var str = x asstring; if (str != null) { // Use str }
การแปลง type โดยใช้ as จะช่วยลดขั้นตอน type checking และไม่มีทาง error เนื่องจาก operator as จะคืนค่า null ในกรณีที่ไม่สามารถแปลง type จะต่างจาก cast ที่จะเกิด error InvalidCastException ข้อจำกัดของ as คือใช้ได้กับ type ที่เป็น Nullable เท่านั้น
ใช้ as ใน for
1 2 3
for (var str = x asstring; str != null; str = null) { // Use str }
เราสามารถใช้ as ว่างไว้ใน expression for เพื่อกันตังแปร str ให้อยู่ใน scope วิธีนี้เป็น programming trick ที่ทำให้โค้ดอ่านยาก จึงไม่นิยมใช้
Performance
การ convert type มีผลกับประสิทธิภาพของโปรแกรม ถ้าไม่สามารถหลีกเลี่ยงได้ ก็ควรทำให้น้อยที่สุด การใช้ as แทน cast จะช่วยลดจำนวน conversion ได้หนึ่งครั้ง เนื่องจากการทำ type checking ด้วย is จะเกิด type conversion เช่นกัน
อย่างไรก็ตามการใช้ as กับข้อมูลประเภท Nullable ของ value type เช่น int? จะ drop performance ของโปรแกรมมากกว่าการใช้ is + cast อย่างมีนัยยสำคัญ เนื่องจากการแปลง value type ให้เป็น Nullable จะมี operator อื่นเพิ่มเข้ามา
Pattern matching แบบแรกคือ ใช้วิธีีคำนวณตัวเลขทีตำแหน่ง matching case โดยใช้ when guard
1 2 3 4 5 6 7 8 9 10
let fizzBuzz number = match number with | n when n % 15 = 0 -> "FizzBuzz" | n when n % 3 = 0 -> "Fizz" | n when n % 5 = 0 -> "Buzz" | n -> sprintf "%d" n
let fizzBuzz i = match i % 3, i % 5with | 0, 0 -> "FizzBuzz" | 0, _ -> "Fizz" | _, 0 -> "Buzz" | _ -> string i [1..100] |> Seq.map fizzBuzz0 |> Seq.iter (printfn "%s")
Partial active pattern.
ใช้ Partial active pattern สองตัว คือ P3 และ P5
1 2 3 4 5 6 7 8 9 10 11 12
let (|P3|_|) i = if i % 3 = 0then Some i else None let (|P5|_|) i = if i % 5 = 0then Some i else None
let f = function | P3 _ & P5 _ -> printfn "FizzBuzz" | P3 _ -> printfn "Fizz" | P5 _ -> printfn "Buzz" | x -> printfn "%d" x
Seq.iter f {1..100} //or for i in1..100do f i
หรือใช้ Partial case เดียว คือ DivisibleBy ซึ่งเป็น Optimize version ของแบบแรก
1 2 3 4 5 6 7 8
let (|DivisibleBy|_|) divisor i = if i % divisor = 0then Some () else None for i in1..100do match i with | DivisibleBy 3 & DivisibleBy 5 -> printfn "FizzBuzz" | DivisibleBy 3 -> printfn "Fizz" | DivisibleBy 5 -> printfn "Buzz" | _ -> printfn "%d" i
typeM<'T> = M of'T typeMonadBuilder() = member this.Return x = M x let m = MonadBuilder() let fizz = function | x when x % 3 = 0 -> m { return x, "Fizz"} | x -> m { return x, x.ToString() } let buzz = function | M (x,s) when x % 5 = 0 -> m { return x, s + "Buzz" } | M (x,"") -> m { return x, x.ToString() } | M (x,s) -> m { return x, s }