ความแตกต่างระหว่างสามวิธีในการกำหนดฟังก์ชันใน Scala


92

กำหนดสามวิธีในการแสดงฟังก์ชันเดียวกันf(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

คำจำกัดความเหล่านี้แตกต่างกันอย่างไร? REPL ไม่ได้ระบุถึงความแตกต่างที่ชัดเจน:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

11
คุณควรสังเกตว่าในบล็อกที่ 2 ข้างต้นการประเมินf1ใน REPL จะแสดงค่าคงที่f1ในขณะที่ประเมินf2และf3แสดงผลลัพธ์ของการเรียกใช้วิธีการเหล่านั้น โดยเฉพาะอย่างยิ่งFunction1[Int, Int]อินสแตนซ์ใหม่จะถูกสร้างขึ้นทุกครั้งf2หรือf3ถูกเรียกใช้ในขณะที่f1จะเหมือนเดิมFunction1[Int, Int]ตลอดไป
Randall Schulz

@RandallSchulz เนื่องจากเวอร์ชัน val ไม่ต้องการอินสแตนซ์ฟังก์ชันใหม่ทำไมจึงใช้ def ในกรณีนี้
Virtualeyes

2
@virtualeyes สถานการณ์เดียวที่ฉันจำได้เมื่อมีคนเห็นว่า def ให้ค่า FunctionN [... ] อยู่ในไลบรารีตัวแยกวิเคราะห์ combinator ไม่ใช่เรื่องปกติที่จะเขียนวิธีการที่ให้ฟังก์ชันและแทบจะไม่ใช้ def เพื่อให้ได้สำเนาของฟังก์ชันที่ไม่มีการเปลี่ยนแปลงทางความหมาย / เชิงฟังก์ชันจำนวนมาก
Randall Schulz

คำตอบ:


112

f1 เป็นฟังก์ชันที่รับจำนวนเต็มและส่งกลับจำนวนเต็ม

f2เป็นวิธีการที่มีค่าศูนย์ที่ส่งกลับฟังก์ชันที่ใช้จำนวนเต็มและส่งกลับจำนวนเต็ม (เมื่อคุณพิมพ์f2ที่ REPL ในภายหลังมันจะกลายเป็นการเรียกใช้เมธอดf2)

f3เหมือนกับf2. คุณไม่ได้ใช้การอนุมานประเภทที่นั่น


6
ทำไมถึงf1เป็นfunctionและf2เป็นmethod?
Freewind

17
@Freewind, applyฟังก์ชั่นเป็นวัตถุที่มีวิธีการตั้งชื่อ วิธีการก็คือวิธีการ
missingfaktor

คำตอบที่ยอดเยี่ยม คำถาม: คุณบอกว่า f2 มีค่าความเที่ยงตรงเป็นศูนย์ แต่มันไม่ใช่ยูนารี? en.wikipedia.org/wiki/Arity "ฟังก์ชัน nullary จะไม่มีอาร์กิวเมนต์ฟังก์ชันยูนารีใช้อาร์กิวเมนต์เดียว" แค่สงสัย!
Matthew Cornell

5
@MatthewCornell f2เองยอมรับไม่มีข้อโต้แย้ง วัตถุฟังก์ชันที่ส่งกลับไม่
missingfaktor

122

ภายในคลาสvalจะได้รับการประเมินจากการเริ่มต้นในขณะที่defได้รับการประเมินเมื่อและทุกครั้งฟังก์ชันจะถูกเรียกใช้ ในโค้ดด้านล่างคุณจะเห็นว่า x ได้รับการประเมินในครั้งแรกที่ใช้ออบเจ็กต์ แต่จะไม่พบอีกครั้งเมื่อมีการเข้าถึงสมาชิก x ในทางตรงกันข้าม y จะไม่ถูกประเมินเมื่ออ็อบเจ็กต์ถูกสร้างอินสแตนซ์ แต่จะถูกประเมินทุกครั้งที่มีการเข้าถึงสมาชิก

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

@JacobusR เป็นความจริงเฉพาะในชั้นเรียนหรือไม่?
Andrew Cassidy

ตัวอย่างเช่น: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 ฉันคาดหวังว่า a (5) จะส่งคืน 10 และค่าของ b จะถูกอินไลน์
Andrew Cassidy

@AndrewCassidy ฟังก์ชันaไม่เปลี่ยนรูปและประเมินได้เมื่อเริ่มต้น แต่bยังคงเป็นค่าที่ไม่แน่นอน ดังนั้นการอ้างอิงถึงbจึงถูกตั้งค่าในระหว่างการเริ่มต้น แต่ค่าที่จัดเก็บโดยbยังคงไม่แน่นอน เพื่อความสนุกสนานคุณสามารถสร้างไฟล์val b = 123. หลังจากนี้คุณa(5)จะให้ 11 เสมอเนื่องจากbตอนนี้เป็นค่าใหม่ทั้งหมด
แจ็ค

@JacobusR ขอบคุณ ... สิ่งนี้สมเหตุสมผล สิ่งนี้เกิดขึ้นพร้อมกับคำจำกัดความของ "ขอบเขตศัพท์" เนื่องจากฟังก์ชัน a มีการอ้างอิงถึง "var b" ดั้งเดิม ฉันเดาว่าสิ่งที่ทำให้ฉันสับสนคือพูดว่า: var b = 5; วาล c = b; b = 6; ทำหน้าที่แตกต่างกัน ฉันเดาว่าฉันไม่ควรคาดหวังว่าคำจำกัดความของฟังก์ชันซึ่งมีการอ้างอิงถึงขอบเขต "ศัพท์" ดั้งเดิมให้ทำงานในลักษณะเดียวกับ Int
Andrew Cassidy

3

การดำเนินการความหมายเช่นdef x = อีจะไม่ประเมินการแสดงออกอี แต่อีได้รับการประเมินเมื่อใดก็ตามที่xถูกนำมาใช้ หรืออีกวิธีหนึ่ง Scala เสนอนิยามค่า val x = eซึ่งจะประเมินeด้านขวามือเป็นส่วนหนึ่งของการประเมินคำจำกัดความ ถ้าxถูกใช้ในภายหลังมันจะถูกแทนที่ด้วยค่าที่คำนวณล่วงหน้าของe ทันทีดังนั้นจึงไม่จำเป็นต้องประเมินนิพจน์อีก

Scala ตามตัวอย่างโดยMartin Odersky

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.