อะไรคือปัญหาที่ได้รับการบรรเทาโดยไม่อนุญาตให้มีการประกาศฟังก์ชันที่ซ้อนกันใน Go?


87

Lambdas ทำงานตามที่คาดไว้:

func main() {
    inc := func(x int) int { return x+1; }
}

อย่างไรก็ตามการประกาศต่อไปนี้ในการประกาศไม่ได้รับอนุญาต:

func main() {
    func inc(x int) int { return x+1; }
}

ด้วยเหตุผลใดจึงไม่อนุญาตให้ใช้ฟังก์ชันซ้อนกัน


อืมฉันไม่รู้ว่าคุณตั้งใจจะทำหรือ func main() { func (x int) int { return x+1; }(3) }
เปล่า

@ ยาสิรก. แต่นั่นก็เป็นแลมด้าเช่นกันไม่ใช่เหรอ? ฉันไม่เข้าใจความคิดเห็นของคุณ ...
corazza

ฟังก์ชันใดที่จะเปิดใช้งานตัวอย่างที่สองในไวยากรณ์ที่อนุญาตซึ่งกรณีแรกไม่รองรับ
Not_a_Golfer

@yannbane มันเป็นนิพจน์แลมบ์ดาฉันไม่คิดว่าคุณจะประกาศฟังก์ชันภายในฟังก์ชันอื่นเช่น JS ได้ ดังนั้นฉันว่าสิ่งที่ดีที่สุดของคุณคือใช้ lambdas
ymg

@Not_a_Golfer: ความเป็นไปได้อย่างหนึ่งที่จะนำไปใช้ในลักษณะที่ JavaScript ทำโดยพื้นฐานแล้วการกำหนดฟังก์ชันให้กับตัวแปรนั้นแตกต่างจากการประกาศฟังก์ชันมากเนื่องจากการไหลของการควบคุมมีผลต่อตัวแปรดังกล่าวในขณะที่ฟังก์ชันใน JavaScript ไม่ได้รับผลกระทบ นั่นหมายความว่าคุณสามารถเรียกinc()ในตัวอย่างที่สองก่อนการประกาศจริง แต่! ฉันกำลังมองหาเหตุผลฉันไม่รู้มากเกี่ยวกับ Go แต่ฉันต้องการเรียนรู้ว่าตรรกะเบื้องหลังกฎนี้คืออะไร
corazza

คำตอบ:


54

ฉันคิดว่ามีสาเหตุ 3 ประการที่ไม่อนุญาตให้ใช้คุณลักษณะที่ชัดเจนนี้

  1. มันจะทำให้คอมไพเลอร์ซับซ้อนเล็กน้อย ในขณะที่คอมไพเลอร์รู้ว่าฟังก์ชันทั้งหมดอยู่ในระดับบนสุด
  2. มันจะทำให้เกิดข้อผิดพลาดของโปรแกรมเมอร์ระดับใหม่ - คุณสามารถ refactor บางสิ่งบางอย่างและซ้อนฟังก์ชันบางอย่างโดยบังเอิญ
  3. การมีไวยากรณ์ที่แตกต่างกันสำหรับฟังก์ชันและการปิดเป็นสิ่งที่ดี การปิดฝาอาจมีราคาแพงกว่าการสร้างฟังก์ชันดังนั้นคุณควรรู้ว่าคุณกำลังทำอยู่

นี่เป็นเพียงความคิดเห็นของฉัน - ฉันไม่ได้เห็นการออกเสียงอย่างเป็นทางการจากนักออกแบบภาษา


2
ปาสคาล (อย่างน้อยก็คือการเกิดเดลฟี) ทำให้ถูกต้องและเรียบง่าย: ฟังก์ชันที่ซ้อนกันจะทำงานเหมือนปกติ แต่ยังสามารถเข้าถึงตัวแปรในขอบเขตของฟังก์ชันปิดล้อมได้ ฉันไม่คิดว่าสิ่งเหล่านี้ยากที่จะนำไปใช้ ในทางกลับกันการเขียนโค้ดเดลฟีจำนวนมากฉันไม่แน่ใจว่าฉันต้องการฟังก์ชันซ้อนกันอย่างไม่ดี: บางครั้งพวกเขารู้สึกดี แต่พวกเขามักจะระเบิดฟังก์ชันปิดล้อมทำให้อ่านแทบไม่ได้ นอกจากนี้การเข้าถึงอาร์กิวเมนต์ของผู้ปกครองอาจทำให้โปรแกรมอ่านยากเนื่องจากตัวแปรเหล่านี้ถูกเข้าถึงโดยปริยาย (ไม่ได้ส่งผ่านเป็นพารามิเตอร์ที่เป็นทางการ)
kostix

1
ฟังก์ชั่นท้องถิ่นนั้นยอดเยี่ยมในฐานะขั้นตอนการปรับโครงสร้างขั้นกลางในการแยกวิธีการของคุณ ใน c # พวกเขาทำให้สิ่งเหล่านี้มีค่ามากขึ้นเมื่อพวกเขาแนะนำฟังก์ชันโลคัลแบบคงที่ซึ่งไม่ได้รับอนุญาตให้จับตัวแปรจากฟังก์ชันการปิดล้อมดังนั้นคุณจึงถูกบังคับให้ส่งอะไรเป็นพารามิเตอร์ ฟังก์ชั่นในพื้นที่แบบคงที่ทำให้จุดที่ 3 ไม่ใช่ปัญหา จุดที่ 2 ก็ไม่ใช่ปัญหาจากมุมมองของฉัน
Cosmin

47

แน่นอนว่าพวกเขาเป็น คุณต้องกำหนดให้กับตัวแปร:

func main() {
    inc := func(x int) int { return x+1; }
}

5
ที่น่าสังเกตคุณไม่สามารถเรียกใช้ฟังก์ชันดังกล่าว (inc) ซ้ำได้
Mohsin Kale

1
เพื่อที่จะเรียกมันซ้ำคุณต้องส่งต่อประกาศvar inc func(int) int
Izana

28

คำถามที่พบบ่อย (FAQ)

ทำไม Go ถึงไม่มีฟีเจอร์ X?

ทุกภาษามีคุณลักษณะใหม่ ๆ และละเว้นคุณลักษณะโปรดของใครบางคน Go ได้รับการออกแบบโดยคำนึงถึงความเป็นไปได้ของการเขียนโปรแกรมความเร็วในการรวบรวมความคิดมุมฉากของแนวคิดและความจำเป็นในการสนับสนุนคุณลักษณะต่างๆเช่นการทำงานพร้อมกันและการเก็บขยะ ฟีเจอร์โปรดของคุณอาจหายไปเนื่องจากไม่พอดีเนื่องจากมีผลต่อความเร็วในการรวบรวมหรือความชัดเจนของการออกแบบหรือเนื่องจากจะทำให้โมเดลระบบพื้นฐานยากเกินไป

หากรบกวนคุณที่ Go ไม่มีฟีเจอร์ X โปรดยกโทษให้เราและตรวจสอบคุณสมบัติที่ Go มี คุณอาจพบว่าพวกเขาชดเชยด้วยวิธีที่น่าสนใจสำหรับการขาด X

อะไรจะแสดงให้เห็นถึงความซับซ้อนและค่าใช้จ่ายในการเพิ่มฟังก์ชันที่ซ้อนกัน เหยาต้องการทำอะไรที่คุณไม่สามารถทำได้หากไม่มีฟังก์ชันซ้อนกัน? และอื่น ๆ


19
เพื่อความเป็นธรรมฉันไม่คิดว่าจะมีใครแสดงให้เห็นถึงความซับซ้อนใด ๆ ที่ทำให้เกิดฟังก์ชันซ้อนกันได้ นอกจากนี้ในขณะที่ฉันเห็นด้วยกับปรัชญาที่ยกมา แต่ฉันก็ไม่แน่ใจว่าการอ้างถึงฟังก์ชันซ้อนกันเป็น "คุณลักษณะ" นั้นมีเหตุผลมากพอ ๆ กับการอ้างถึงการละเว้นเป็นคุณลักษณะ คุณรู้หรือไม่ว่ามีภาวะแทรกซ้อนใด ๆ ที่อนุญาตให้ใช้ฟังก์ชันซ้อนกันได้ ฉันสมมติว่าพวกเขาเป็นเพียงแค่น้ำตาลในการสังเคราะห์สำหรับ lambdas (ฉันไม่สามารถนึกถึงพฤติกรรมที่สมเหตุสมผลอื่น ๆ ได้)
joshlf

เนื่องจาก go ถูกรวบรวมวิธีเดียวที่จะทำ AFAIK นี้จะสร้างไวยากรณ์อื่นสำหรับกำหนด lambdas และฉันไม่เห็นกรณีการใช้งานสำหรับสิ่งนั้นจริงๆ คุณไม่สามารถมีฟังก์ชันคงที่ภายในฟังก์ชันแบบคงที่ที่สร้างขึ้นแบบเรียลไทม์ - จะเกิดอะไรขึ้นถ้าเราไม่ป้อนเส้นทางรหัสเฉพาะที่กำหนดฟังก์ชัน?
Not_a_Golfer

เพียงแค่ส่งผ่าน lambda interface {} แล้วพิมพ์ assert เช่น. f_lambda: = lambda (func () rval {}) หรืออะไรก็ตามที่ต้นแบบจะเป็น ไวยากรณ์การปฏิเสธ func ไม่รองรับสิ่งนี้ แต่เป็นภาษาทั้งหมด
BadZen


8

อนุญาตให้ใช้ฟังก์ชันที่ซ้อนกันใน Go คุณเพียงแค่กำหนดให้กับตัวแปรท้องถิ่นภายในฟังก์ชันภายนอกและเรียกใช้โดยใช้ตัวแปรเหล่านั้น

ตัวอย่าง:

func outerFunction(iterations int, s1, s2 string) int {
    someState := 0
    innerFunction := func(param string) int {
        // Could have another nested function here!
        totalLength := 0
        // Note that the iterations parameter is available
        // in the inner function (closure)
        for i := 0; i < iterations; i++) {
            totalLength += len(param)
        }
        return totalLength
    }
    // Now we can call innerFunction() freely
    someState = innerFunction(s1)
    someState += innerFunction(s2)
    return someState
}
myVar := outerFunction(100, "blah", "meh")

ฟังก์ชั่นภายในมักมีประโยชน์สำหรับ goroutines ในท้องถิ่น:

func outerFunction(...) {
    innerFunction := func(...) {
        ...
    }
    go innerFunction(...)
}

การปิดระหว่างใช้งานจะแตกต่างจากฟังก์ชันธรรมดาในบางประการ ตัวอย่างเช่นคุณไม่สามารถเรียกการปิดซ้ำได้
Michał Zabielski

7
@ MichałZabielski: คุณสามารถเรียกมันซ้ำได้ถ้าคุณประกาศก่อนกำหนด
P Daddy

1

คุณต้องเรียกมันทันทีโดยเพิ่ม()ที่ส่วนท้าย

func main() {
    func inc(x int) int { return x+1; }()
}

แก้ไข: ไม่สามารถมีชื่อฟังก์ชั่น ... ดังนั้นจึงเป็นเพียง lambda func ที่ถูกเรียกทันที:

func main() {
    func(x int) int { return x+1; }()
}

1
เอ่อที่ไม่เป็นไปตามที่ใคร ๆ คาดหวังจากนิยามฟังก์ชัน
corazza

1
@corazza อ่าคิดถึงคำว่าประกาศตอนอ่านคำถาม ความผิดฉันเอง.
นิค

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