8. Strictness and Laziness - Gaoey/scala-diary GitHub Wiki

Strict คือ Evaluate(ประเมิณผล) the expression now Lazy คือ Evaluate it on the first value now

**พิสูจน์ Evaluation Scala เป็น Strict **

ยกตัวอย่างโดยใช้ Variable Assignment

scala> val s = fac(15)/fac(11)
> you call fac for 15
> you call fac for 11
> s: Int = 50

scala> println(s)
50

สังเกตุได้ว่า scala ไม่ได้ทำการคำนวนใหม่ เพราะถูกคำนวนตั้งแต่ประกาศ Expression แล้ว แสดงว่า scala ทำการคำนวนครั้งแรกครั้งเดียว(initialized once) และเอาค่าที่คำนวนแล้วกลับมาใช้ใหม่ (reusing)

ยกตัวอย่างโดยใช้ Function Parameter

scala> def twice(i: Int) = {
 println("We haven't used yet")
 i + i
}

scala> twice(fac(15)/fac(11))
> you call fac for 15
> you call fac for 11
> We haven't use yet
res2: Int = 100

สังเกตุได้ว่าพอโยน expression fac(15)/fac(11) เข้า parameter ทำการ evaluate expression ก่อน ค่อยคำนวน i

ยกตัวอย่างโดยใช้ HOF

scala> def twice(f: => Int) = {
 println("We haven't used yet")
 f+f
}

scala> twice(fac(15)/fac(11))
> We haven't used yet
> you call fac for 15
> you call fac for 11
> you call fac for 15
> you call fac for 11
res2: Int = 100

สังเกตุมั้ยว่ายิ่งมีการเรียก f มากเท่าไหร่ จะมีการเรียก expression fac มากขึ้นเท่านั้น ทำให้เกิด performance problem

วิธีแก้ ใช้ cache มัน

scala> def twice(f: => Int) = {
 val i = f
 println("We haven't used yet")
 i+i
}

scala> twice(fac(15)/fac(11))
> you call fac for 15
> you call fac for 11
> We haven't used yet
> res5: Int = 100

Scala ทำการ Evaluate ด้วยวิธี Strict Evaluation ซึ่งคือการ evaluate function parameter ก่อนจะ passing value ไปที่ function ซึ่งถ้า parameter value เป็น function มันก็ทำการ evaluate ทุกครั้งที่มีการเรียกใช้ function นั้นๆ ถ้าไม่อยากให้มัน evaluate ตลอด ก็ cache(เก็บค่าไว้ก่อน)ซะ


Lazy Evaluation ตรงข้ามกับ Strict evaluation ตรงที่ Strict จะทำการ Evaluate เลยถ้ามีการประกาศ expression แต่ Lazy จะไม่ evaluate ถ้าไม่มีการเรียกใช้

scala>lazy val l = fac(15)/fac(11)
l: Int = <lazy>

scala> val s = fac(15)/fac(11)
> you call fac for 15
> you call fac for 11
> s: Int = 50

scala> println(l)
> you call fac for 15
> you call fac for 11
> 50

scala> println(l)
> 50

จะเห็นได้ว่าถ้า Lazy variable มีการทำงานแล้วก็จะเก็บค่านั้นๆไว้ตั้งแต่การ evaluate ครั้งแรก (initialize once) Lazy คือ Evaluate it on the first value now

มาดูการใช้ Lazy ใน HOF

scala> def twice(f: => Int) = {
 lazy val i = f
 println("We haven't used yet")
 i+i
}

scala> twice(fac(15)/fac(11))
> We haven't used yet
> you call fac for 15
> you call fac for 11
> res5: Int = 100

เทียบกับฝั่ง strict ใน HOF

scala> def twice(f: => Int) = {
 val i = f
 println("We haven't used yet")
 i+i
}

scala> twice(fac(15)/fac(11))
> you call fac for 15
> you call fac for 11
> We haven't used yet
> res5: Int = 100

แบบ strict จะ evaluate function parameter ก่อนเลย แล้วค่อย passing value แต่แบบ lazy คือ function แบบ strict จะถูกทำงานก่อน จากนั้น parameter แบบ lazy จะ evaluate ตอนถูกใช้งาน


Why Lazy Lazy เหมาะกับงานประเภททีื่มี Large Data มากๆจนไม่รู้จุดที่สิ้นสุดเท่าไหร่แล้วเราต้องการ evaluate เฉพาะเท่าที่ใช้เหล่านั้น lazy จะใช้ก็ต่อเมื่อถูกใช้งาน ไม่ใช่เข้ามาก็ทำงานตลอดเลย หรืองานประเภทเป็น Infinite ทำไปจนไม่รู้จุดสิ้นสุด

ยกตัวอย่าง infinite - Fibonacci Series ถ้าโจทย์คือ generate list ของ fibonacci ทั้งหมด ถ้าใช้วิธีการแบบ Strict อาจจะ out of memory ได้ แต่ถ้าเป็น lazy นั้นทำได้

Stream คือ Lazy list ของ scala

** #:: คือสัญลักษณ์การต่อกัน ของ stream list**

scala> def fibFrom(a: Int, b: Int): Stream[Int] = a #:: fibFrom(b, a+b)
fibFrom:( a:Int, b:Int) Stream[Int]

scala> val f = fibFrom(1,2)
scala> f.takeWhile(_<10) foreach println
1
2
3
5
8
⚠️ **GitHub.com Fallback** ⚠️