อะไรคือความแตกต่างระหว่าง:
def even: Int => Boolean = _ % 2 == 0
และ
val even: Int => Boolean = _ % 2 == 0
even(10)
ทั้งสองสามารถเรียกได้ว่าเหมือน
อะไรคือความแตกต่างระหว่าง:
def even: Int => Boolean = _ % 2 == 0
และ
val even: Int => Boolean = _ % 2 == 0
even(10)
ทั้งสองสามารถเรียกได้ว่าเหมือน
คำตอบ:
เมธอดdef even
ประเมินการโทรและสร้างฟังก์ชันใหม่ทุกครั้ง (อินสแตนซ์ใหม่ของFunction1
)
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
ด้วยdef
คุณสามารถรับฟังก์ชั่นใหม่ทุกการโทร:
val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1049057402
test()
// Int = -1049057402 - same result
def test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -240885810
test()
// Int = -1002157461 - new result
val
ประเมินเมื่อกำหนดdef
- เมื่อเรียกว่า:
scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing
scala> def even: Int => Boolean = ???
even: Int => Boolean
scala> even
scala.NotImplementedError: an implementation is missing
lazy val
ทราบว่ามีตัวเลือกที่สาม:
มันประเมินเมื่อเรียกครั้งแรก:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
แต่ส่งคืนผลลัพธ์เดียวกัน (ในกรณีนี้อินสแตนซ์เดียวกันFunctionN
) ทุกครั้ง:
lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
lazy val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1068569869
test()
// Int = -1068569869 - same result
ประสิทธิภาพ
val
ประเมินเมื่อกำหนด
def
ประเมินการโทรทุกครั้งดังนั้นประสิทธิภาพอาจแย่กว่าval
การโทรหลายครั้ง คุณจะได้รับประสิทธิภาพเดียวกันด้วยการโทรเพียงครั้งเดียว และเมื่อไม่มีสายคุณจะไม่ได้รับค่าใช้จ่ายdef
ดังนั้นคุณสามารถกำหนดได้แม้ว่าคุณจะไม่ได้ใช้ในบางสาขา
ด้วยlazy val
คุณจะได้รับการประเมินผลขี้เกียจ: คุณสามารถกำหนดได้แม้ว่าคุณจะไม่ใช้มันในบางสาขาและจะประเมินครั้งหรือไม่ แต่คุณจะได้รับค่าใช้จ่ายเล็ก ๆ น้อย ๆ lazy val
จากการตรวจสอบคู่ล็อคในการเข้าถึงของคุณทุกคน
ดังที่ @SargeBorsch ตั้งข้อสังเกตว่าคุณสามารถกำหนดวิธีการและนี่คือตัวเลือกที่เร็วที่สุด:
def even(i: Int): Boolean = i % 2 == 0
แต่ถ้าคุณต้องการฟังก์ชั่น (ไม่ใช่วิธี) สำหรับองค์ประกอบหรือฟังก์ชั่นสำหรับฟังก์ชั่นการสั่งซื้อที่สูงขึ้น (ชอบfilter(even)
) val
คอมไพเลอร์จะสร้างฟังก์ชั่นจากวิธีการของคุณทุกครั้งที่คุณจะใช้มันเป็นฟังก์ชั่นเพื่อให้ประสิทธิภาพการทำงานที่อาจจะเลวร้ายยิ่งกว่าเล็กน้อย
even
นั้นสำคัญหรือไม่
def
สามารถใช้เพื่อกำหนดวิธีการและนี่คือตัวเลือกที่เร็วที่สุด @ A.Karimi
even eq even
2.12
@inline
คุณลักษณะสำหรับสิ่งนี้ แต่มันไม่สามารถใช้ฟังก์ชั่นอินไลน์ได้เพราะการเรียกใช้ฟังก์ชั่นเป็นการเรียกapply
ใช้วิธีการเสมือนของวัตถุฟังก์ชั่น JVM อาจ devirtualise และ inline การโทรดังกล่าวในบางสถานการณ์ แต่ไม่ใช่โดยทั่วไป
พิจารณาสิ่งนี้:
scala> def even: (Int => Boolean) = {
println("def");
(x => x % 2 == 0)
}
even: Int => Boolean
scala> val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>
scala> even(1)
def
res9: Boolean = false
scala> even2(1)
res10: Boolean = false
คุณเห็นความแตกต่างหรือไม่ ในระยะสั้น:
def : สำหรับการเรียกทุกครั้งจะeven
เรียกเนื้อความของeven
วิธีการอีกครั้ง แต่ด้วยeven2
ie valฟังก์ชั่นจะเริ่มต้นได้เพียงครั้งเดียวในขณะที่การประกาศ (และด้วยเหตุนี้มันจะพิมพ์val
ที่บรรทัดที่ 4 และไม่เคยอีกครั้ง) และเอาท์พุทเดียวกันจะถูกใช้ในแต่ละครั้งที่มันเข้าถึง ตัวอย่างเช่นลองทำสิ่งนี้:
scala> import scala.util.Random
import scala.util.Random
scala> val x = { Random.nextInt }
x: Int = -1307706866
scala> x
res0: Int = -1307706866
scala> x
res1: Int = -1307706866
เมื่อไหร่ x
มีการเริ่มต้นค่าส่งกลับโดยถูกตั้งค่าเป็นค่าสุดท้ายของRandom.nextInt
x
ครั้งต่อไปx
จะถูกใช้อีกครั้งมันจะคืนค่าเดิมเสมอ
x
นอกจากนี้คุณยังสามารถเริ่มต้นอย่างเฉื่อยชา เช่นครั้งแรกมันถูกใช้มันจะเริ่มต้นและไม่ได้ในขณะที่ประกาศ ตัวอย่างเช่น:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
สองครั้งครั้งด้วยและครั้งเดียวกับ1
2
คุณจะได้รับคำตอบที่แตกต่างกันในการโทรแต่ละครั้ง ดังนั้นในขณะที่println
ไม่ได้ดำเนินการในการโทรตามมาคุณไม่ได้รับเดียวกันผลeven2
จากการโทรที่แตกต่างกันไป สำหรับสาเหตุที่println
ไม่ถูกดำเนินการอีกครั้งนั่นเป็นคำถามที่แตกต่าง
ดูนี่:
var x = 2 // using var as I need to change it to 3 later
val sq = x*x // evaluates right now
x = 3 // no effect! sq is already evaluated
println(sq)
น่าแปลกใจที่สิ่งนี้จะพิมพ์ 4 และไม่ใช่ 9! val (คู่ var) จะถูกประเมินทันทีและกำหนด
ตอนนี้เปลี่ยน val เป็น def .. มันจะพิมพ์ 9! Def คือการเรียกใช้ฟังก์ชัน .. มันจะประเมินทุกครั้งที่เรียกใช้
val เช่น "sq" คือโดยคำจำกัดความ Scala ได้รับการแก้ไข มีการประเมินสิทธิ์ ณ เวลาที่ประกาศคุณไม่สามารถเปลี่ยนแปลงได้ในภายหลัง ในตัวอย่างอื่น ๆ โดยที่ Even2 ยัง val แต่มันประกาศด้วยฟังก์ชั่นลายเซ็นเช่น "(Int => บูลีน)" ดังนั้นจึงไม่ได้เป็นประเภท Int มันเป็นฟังก์ชั่นและมันเป็นค่าที่ถูกกำหนดโดยการแสดงออกดังต่อไปนี้
{
println("val");
(x => x % 2 == 0)
}
ตามคุณสมบัติของ Scala val คุณไม่สามารถกำหนดฟังก์ชันอื่นให้กับ even2 ได้เช่นเดียวกับ sql
เกี่ยวกับสาเหตุที่เรียกฟังก์ชัน eval2 val ไม่พิมพ์ "val" ซ้ำแล้วซ้ำอีก?
รหัส Orig:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
เรารู้ว่าในถ้อยแถลงสุดท้ายของสกาลาเรื่องการแสดงออกเหนือ (ภายใน {.. }) จริง ๆ แล้วกลับไปทางซ้ายมือ ดังนั้นคุณจะสิ้นสุดการตั้งค่า even2 เป็น "x => x% 2 == 0" ฟังก์ชั่นซึ่งตรงกับประเภทที่คุณประกาศสำหรับประเภทคู่ Even2 เช่น (Int => บูลีน) ดังนั้นคอมไพเลอร์จึงมีความสุข ตอนนี้แม้เพียง 2 ชี้ไปที่ "(x => x% 2 == 0)" ฟังก์ชั่น (ไม่มีคำสั่งอื่น ๆ ก่อนเช่น println ("val") ฯลฯ การเรียกใช้ event2 ด้วยพารามิเตอร์ที่แตกต่างกันจะเรียกใช้จริง "(x => x% 2 == 0) "รหัสเนื่องจากจะถูกบันทึกด้วย event2 เท่านั้น
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
เพียงชี้แจงเพิ่มเติมนี้ต่อไปนี้เป็นรหัสรุ่นอื่น
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
อะไรจะเกิดขึ้น ? ที่นี่เราเห็น "ภายในขั้นสุดท้าย fn" พิมพ์อีกครั้งและอีกครั้งเมื่อคุณโทร Even2 ()
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
ดำเนินการคำจำกัดความเช่น def x = e
จะไม่ประเมินนิพจน์ e In- stead e จะถูกประเมินเมื่อใดก็ตามที่ x ถูกเรียกใช้
อีกทางเลือกหนึ่ง Scala เสนอคำนิยามค่า
val x = e
ซึ่งจะประเมินด้านขวามือเป็นส่วนหนึ่งของการประเมินของคำนิยาม ถ้า x ถูกใช้ในภายหลังมันจะถูกแทนที่ทันทีด้วยค่าที่คำนวณล่วงหน้าของ e เพื่อไม่ให้นิพจน์ประเมินอีกครั้ง
นอกจากนี้ Val เป็นการประเมินมูลค่าด้วย ซึ่งหมายความว่านิพจน์ทางด้านขวาจะถูกประเมินระหว่างการกำหนด ตำแหน่ง Def คือการประเมินชื่อ มันจะไม่ประเมินจนกว่าจะมีการใช้งาน
นอกเหนือจากการตอบกลับที่เป็นประโยชน์ข้างต้นแล้วสิ่งที่ฉันค้นพบคือ:
def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int
def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int
def test3(): Int = 4
--test3: test3[]() => Int
ข้างต้นแสดงให้เห็นว่า“ def” เป็นวิธีการ (โดยไม่มีพารามิเตอร์พารามิเตอร์ศูนย์) ที่ส่งกลับฟังก์ชันอื่น "Int => Int" เมื่อเรียกใช้
การอธิบายวิธีการแปลงฟังก์ชั่นเป็นฟังก์ชั่นที่นี่: https://tpolecat.github.io/2014/06/09/methods-functions.html
ใน REPL
scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean
scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8
def หมายถึงcall-by-name
ประเมินตามความต้องการ
val หมายถึงcall-by-value
ประเมินขณะเตรียมใช้งาน
Int => Boolean
หมายถึงอะไร ฉันคิดว่าไวยากรณ์ที่กำหนดคือdef foo(bar: Baz): Bin = expr