ตัวแปรฟังก์ชันคงที่ใน Swift


97

ฉันกำลังพยายามหาวิธีประกาศตัวแปรแบบคงที่ที่กำหนดขอบเขตไว้เฉพาะในฟังก์ชันใน Swift

ใน C อาจมีลักษณะดังนี้:

int foo() {
    static int timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

ใน Objective-C โดยพื้นฐานแล้วจะเหมือนกัน:

- (NSInteger)foo {
    static NSInteger timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

แต่ดูเหมือนฉันจะทำอะไรแบบนี้ใน Swift ไม่ได้ ฉันได้ลองประกาศตัวแปรด้วยวิธีต่อไปนี้:

static var timesCalledA = 0
var static timesCalledB = 0
var timesCalledC: static Int = 0
var timesCalledD: Int static = 0

แต่ทั้งหมดนี้ส่งผลให้เกิดข้อผิดพลาด

  • ข้อแรกบ่นว่า "คุณสมบัติคงที่สามารถประกาศได้เฉพาะประเภท"
  • ข้อที่สองบ่นว่า "การประกาศที่คาดหวัง" (ที่staticอยู่) และ "รูปแบบที่คาดหวัง" (อยู่ที่ไหนtimesCalledB)
  • คำที่สามบ่นว่า "ข้อความต่อเนื่องในบรรทัดต้องคั่นด้วย"; "" (ในช่องว่างระหว่างเครื่องหมายทวิภาคและstatic) และ "ประเภทที่คาดหวัง" (โดยที่static)
  • ข้อที่สี่กล่าวว่า "ข้อความต่อเนื่องในบรรทัดต้องคั่นด้วย"; "" (ในช่องว่างระหว่างIntและstatic) และ "การประกาศที่คาดหวัง" (ภายใต้เครื่องหมายเท่ากับ)

คำตอบ:


159

ฉันไม่คิดว่า Swift รองรับตัวแปรคงที่โดยไม่ต้องเชื่อมต่อกับคลาส / โครงสร้าง ลองประกาศโครงสร้างส่วนตัวด้วยตัวแปรคงที่

func foo() -> Int {
    struct Holder {
        static var timesCalled = 0
    }
    Holder.timesCalled += 1
    return Holder.timesCalled
}

  7> foo()
$R0: Int = 1
  8> foo()
$R1: Int = 2
  9> foo()
$R2: Int = 3

ใช่ฉันเล่นต่อไปอีกเล็กน้อยและนี่ก็เป็นวิธีแก้ปัญหาที่น่าเบื่อจริงๆที่ฉันคิดขึ้นมาเช่นกัน
nhgrif

17
อัปโหลดแล้ว แต่ฉันเสียใจที่เราต้องใช้วิธีนี้
Tricertops

1
คุณสมบัติและวิธีการประเภทเป็นของประเภท (เช่นคลาสโครงสร้างหรือ Enum) และไม่สามารถเป็นของฟังก์ชันเพียงอย่างเดียว เอกสารเกี่ยวกับแอปเปิ้ลประเภทอสังหาริมทรัพย์ @Tricertops. อีกวิธีหนึ่งในการดำเนินการนี้คือการใส่ฟังก์ชัน "foo" ในคลาสสร้างคุณสมบัติ type สำหรับคลาสนั้นและใช้ภายในฟังก์ชัน
NSCoder

6
@NSCoder แต่เป็นไปได้ที่จะประกาศstruct Holder {…}ภายในฟังก์ชั่นต่างๆและจะไม่ชนกัน Swift สามารถรองรับได้static letหากไม่มีแผ่นstructสำเร็จรูปนี้
Tricertops

1
@ สวัสดีฉันขอโทษ แต่ฉันไม่พบคำตอบอื่น ๆ ที่อัปเดตเพิ่มเติม?
Bryan Chen

23

อีกวิธีหนึ่ง

func makeIncrementerClosure() -> () -> Int {
    var timesCalled = 0
    func incrementer() -> Int {
        timesCalled += 1
        return timesCalled
    }
    return incrementer
}

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2

3
นี่เป็นวิธีใช้จาวาสคริปต์ทั่วไป
Bryan Chen

1
แต่ถ้าฉันเรียก ba () อีกครั้งฟังก์ชันภายในจะคืนค่า 1 ในการโทรครั้งแรก ซึ่งแตกต่างจากตัวแปรคงที่
nhgrif

2
นอกจากนี้ยังมีการเรียนการสอนในเอกสารของ Apple ที่นี่: developer.apple.com/library/ios/documentation/Swift/Conceptual/… ดูเหมือนว่าจะเป็นทางออกที่ดีที่สุดเพียงเพื่อให้สอดคล้องกับ "การเขียนโปรแกรมเชิงฟังก์ชัน" แต่ยังมีโซลูชันอื่น ๆ เช่น ดี. นี่ควรเป็นคำตอบที่ยอมรับได้
datWooWoo

1
ฉันขอโทษ แต่นี่เป็นการแฮ็กที่น่าเกลียดซึ่งเพิ่มความซับซ้อนให้กับปัญหาเดียวกัน ประเด็นของคุณคืออะไร? ในกรณีนั้นฉันชอบคุณสมบัติระดับธรรมดา คำตอบของ @Brian Chen คือคำตอบที่ใกล้เคียงที่สุดที่คุณจะได้รับ ฉันใช้คำตอบของเขาสำหรับวิธีแก้ปัญหาแบบฟลิปฟล็อป Daniel อาจเป็นคนที่สอดคล้องกับกฎการเขียนโปรแกรม Swift ของ Apple ได้ดีที่สุด
AndaluZ

1
ฉันชอบโซลูชันนี้เป็นพิเศษ นี่เป็นตัวอย่างที่สมบูรณ์แบบของการใช้ฟังก์ชันลำดับที่สูงกว่าเพื่อให้ได้ผลลัพธ์เช่นเดียวกับตัวแปรคงที่ภายในขอบเขตของฟังก์ชัน ฟังก์ชันคงที่ vars ไม่ได้รับการสนับสนุนใน Swift ด้วยเหตุผลที่ดี มันเป็นวิวัฒนาการตามธรรมชาติของการเขียนโปรแกรม การพยายามเขียนโค้ดในรูปแบบเก่า ๆ จำเป็นต้องมีการแฮ็ก ในความคิดของฉันการเพิ่มประเภทข้อมูลที่ซ้อนกันเป็นพิเศษเมื่อเทียบกับการใช้การจับตัวแปรจะช่วยลดความสามารถในการอ่านโค้ด
nstein

18

ตอนนี้ Swift 1.2 พร้อม Xcode 6.3 รองรับแบบคงที่ตามที่คาดไว้ จากบันทึกย่อประจำรุ่น Xcode 6.3 beta:

วิธีการและคุณสมบัติ "คงที่" สามารถใช้ได้ในคลาสแล้ว (เป็นนามแฝงสำหรับ "คลาสสุดท้าย") ตอนนี้คุณได้รับอนุญาตให้ประกาศคุณสมบัติที่จัดเก็บแบบคงที่ในคลาสซึ่งมีพื้นที่เก็บข้อมูลส่วนกลางและเริ่มต้นอย่างเฉื่อยชาในการเข้าถึงครั้งแรก (เช่นตัวแปรส่วนกลาง) ปัจจุบันโปรโตคอลประกาศข้อกำหนดประเภทเป็นข้อกำหนด "คงที่" แทนที่จะประกาศเป็นข้อกำหนด "คลาส" (17198298)

ดูเหมือนว่าฟังก์ชันจะไม่มีการประกาศแบบคงที่ (ตามที่ถามในคำถาม) แต่การประกาศจะต้องทำในระดับชั้นเรียน

ตัวอย่างง่ายๆที่แสดงคุณสมบัติคงที่เพิ่มขึ้นภายในฟังก์ชันคลาส (aka static) แม้ว่าจะไม่จำเป็นต้องใช้ฟังก์ชันคลาส:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

StaticThing.doSomething()
StaticThing.doSomething()
StaticThing.doSomething()

เอาท์พุต:

1
2
3

1
ฉันสงสัยว่าความแตกต่างในความหมายนี้staticอาจมีเจตนาในส่วนของ Apple แม้ว่าเราจะยินดีให้เรายื่นข้อบกพร่องเพื่อขอการเปลี่ยนแปลง ใน C staticจำกัด การจัดเก็บตัวแปรไว้ที่ขอบเขตของไฟล์ต้นฉบับ (ซึ่งไม่เหมือนกับขอบเขตคลาสเสมอไป) ในขณะที่ตำแหน่งของการประกาศตัวแปรจะกำหนดขอบเขตคำศัพท์ (เช่น global vs within function vs many-nested- {}s) ใน Swift ขอบเขตการจัดเก็บจะเป็นไปตามขอบเขตคำศัพท์เสมอดังนั้นคุณจึงไม่สามารถมีตัวแปรที่เป็นคำศัพท์สำหรับฟังก์ชันและที่มีพื้นที่เก็บข้อมูลส่วนกลาง
rickster

5
แดเนียลนี่เป็นเรื่องละเอียดอ่อน (แต่ที่สำคัญ) แตกต่างจากคำถามที่ถาม ฉันขอขอบคุณสำหรับคำตอบ @rickster ฉันเข้าใจสิ่งที่คุณพูดและคิดว่าความคิดเห็นของคุณสามารถขยายเป็นคำตอบที่ดีสำหรับคำถามนี้
nhgrif

@nhgrif ใช่ฉันระบุในคำตอบว่านี่ไม่ได้ตอบคำถามเฉพาะ ฉันแค่คิดว่าการเปลี่ยนแปลงใน Swift 1.2 ตอบสนองความต้องการหลักสำหรับกรณีการใช้งานนี้ (แน่นอนว่าเรื่องราวที่ดีกว่า Swift 1.2 ก่อนหน้านี้) แต่ดูเหมือนว่าสิ่งสำคัญสำหรับคุณที่จะต้องกำหนดขอบเขตตัวแปรให้กับฟังก์ชันซึ่งในปัจจุบันไม่สามารถทำได้
Daniel

@rickster ใน CI คิดว่าคงถูกเก็บไว้ทั่วโลกจริงๆ ฉันไม่แน่ใจว่า ฉันคิดว่านั่นเป็นปัญหาที่ Apple พยายามแก้ไขที่นี่ อย่างรวดเร็วตอนนี้มีการใช้คำศัพท์และพื้นที่จัดเก็บข้อมูลอยู่ในชั้นเรียน
เสมอ

@nhgrif ตามความคิดเห็นก่อนหน้าของฉันฉันคิดว่าคำตอบของ Daniel ควรเป็นคำตอบที่ยอมรับได้จริง ๆ เพราะถึงแม้ว่าคุณจะสามารถประกาศตัวแปรแบบคงที่ในฟังก์ชัน objc ได้ในรูปแบบคำศัพท์ แต่ก็ไม่ได้กำหนดขอบเขตไว้ที่นั่นซึ่งมีผลเหมือนกับการใช้ Static Type ทรัพย์สินอย่างรวดเร็ว ข้อแตกต่างเพียงอย่างเดียวคือจุดประกาศที่รวดเร็วนั้นอธิบายได้มากกว่าและไม่ทำให้เข้าใจผิดว่าขอบเขตของตัวแปรคืออะไร
BTRUE

0

อีกวิธีหนึ่ง

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.