ทำไมแลมบ์ดาของ C ++ 11 ต้องการคำหลัก“ ไม่แน่นอน” สำหรับการจับภาพโดยค่าเริ่มต้น


256

ตัวอย่างสั้น ๆ :

#include <iostream>

int main()
{
    int n;
    [&](){n = 10;}();             // OK
    [=]() mutable {n = 20;}();    // OK
    // [=](){n = 10;}();          // Error: a by-value capture cannot be modified in a non-mutable lambda
    std::cout << n << "\n";       // "10"
}

คำถาม: ทำไมเราต้องการmutableคำหลัก มันค่อนข้างแตกต่างจากการส่งพารามิเตอร์แบบดั้งเดิมไปยังฟังก์ชั่นที่ตั้งชื่อ เหตุผลเบื้องหลังคืออะไร

ฉันรู้สึกว่าจุดจับภาพโดยรวมคือการอนุญาตให้ผู้ใช้เปลี่ยนชั่วคราว - ไม่เช่นนั้นฉันเกือบจะดีกว่าเสมอในการใช้การจับภาพอ้างอิงโดยอ้างอิงใช่ไหม?

ตรัสรู้ใด ๆ

(ฉันใช้ MSVC2010 โดยวิธี AFAIK นี้ควรเป็นมาตรฐาน)


101
คำถามที่ดี; แม้ว่าฉันจะดีใจที่บางสิ่งบางอย่างเป็นconstค่าเริ่มต้นในที่สุด!
xtofl

3
ไม่ใช่คำตอบ แต่ฉันคิดว่านี่เป็นสิ่งที่สมเหตุสมผล: ถ้าคุณรับบางสิ่งบางอย่างตามตัวอักษรคุณไม่ควรเปลี่ยนมันเพื่อประหยัด 1 สำเนาลงในตัวแปรท้องถิ่น อย่างน้อยคุณจะไม่ทำผิดพลาดในการเปลี่ยน n โดยแทนที่ = ด้วย &
stefaanv

8
@xtofl: ไม่แน่ใจว่ามันดีเมื่อค่าอื่น ๆ ไม่ใช่constค่าเริ่มต้น
kizzx2

8
@ Tamás Szelei: อย่าเริ่มต้นการโต้แย้ง แต่ IMHO แนวคิด "ง่ายต่อการเรียนรู้" ไม่มีที่ในภาษา C ++ โดยเฉพาะในยุคปัจจุบัน อย่างไรก็ตาม: P
kizzx2

3
"จุดรวมของการดักจับโดยค่าคือการอนุญาตให้ผู้ใช้เปลี่ยนชั่วคราว" - ไม่จุดทั้งหมดคือแลมบ์ดาอาจยังคงใช้ได้ตลอดอายุการใช้งานของตัวแปรที่จับได้ ถ้า C ++ lambdas มีการดักจับโดยอ้างอิงเท่านั้นมันจะไม่สามารถใช้ได้ในหลาย ๆ สถานการณ์
เซบาสเตียนเรดล

คำตอบ:


230

มันต้องการmutableเพราะโดยค่าเริ่มต้นวัตถุฟังก์ชั่นควรให้ผลลัพธ์เดียวกันทุกครั้งที่มันถูกเรียก นี่คือความแตกต่างระหว่างฟังก์ชั่นการจัดวางวัตถุและฟังก์ชั่นที่ใช้ตัวแปรทั่วโลกได้อย่างมีประสิทธิภาพ


7
นี่เป็นจุดที่ดี ฉันเห็นด้วยอย่างยิ่ง ใน C ++ 0x ฉันไม่ค่อยเห็นว่าค่าเริ่มต้นจะช่วยบังคับใช้ข้างต้นได้อย่างไร void f(const std::function<int(int)> g)พิจารณาผมที่สิ้นสุดรับของแลมบ์ดาเช่นผม ฉันจะรับประกันได้อย่างไรว่าการอ้างอิงgจริงโปร่งใส ? gซัพพลายเออร์ของอาจจะใช้mutableต่อไป ดังนั้นฉันจะไม่รู้ ในทางตรงกันข้ามถ้าเริ่มต้นคือไม่ใช่constและคนที่ต้องเพิ่มconstแทนการmutableไปยังวัตถุที่ฟังก์ชั่นคอมไพเลอร์จริงสามารถบังคับใช้const std::function<int(int)>ส่วนหนึ่งและตอนนี้fสามารถสรุปได้ว่าgเป็นconstไม่?
kizzx2

8
@ kizzx2: ใน C ++ ไม่มีการบังคับใช้อะไรแนะนำเท่านั้น ตามปกติถ้าคุณทำอะไรโง่ ๆ (ต้องการเอกสารเพื่อความโปร่งใสในการอ้างอิงแล้วส่งผ่านฟังก์ชั่นที่ไม่อ้างอิงความโปร่งใส) คุณจะได้อะไรก็ตามที่มากับคุณ
ลูกสุนัข

6
คำตอบนี้เปิดตาของฉัน ก่อนหน้านี้ฉันคิดว่าในกรณีนี้แลมบ์ดากลายพันธุ์เพียงสำเนาสำหรับ "วิ่ง" ปัจจุบัน
Zsolt Szatmari

4
@ZsoltSzatmari ความคิดเห็นของคุณเปิดตาของฉัน! : -DI ไม่ได้รับความหมายที่แท้จริงของคำตอบนี้จนกว่าฉันจะอ่านความคิดเห็นของคุณ
Jendas

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

103

รหัสของคุณใกล้เคียงกับสิ่งนี้:

#include <iostream>

class unnamed1
{
    int& n;
public:
    unnamed1(int& N) : n(N) {}

    /* OK. Your this is const but you don't modify the "n" reference,
    but the value pointed by it. You wouldn't be able to modify a reference
    anyway even if your operator() was mutable. When you assign a reference
    it will always point to the same var.
    */
    void operator()() const {n = 10;}
};

class unnamed2
{
    int n;
public:
    unnamed2(int N) : n(N) {}

    /* OK. Your this pointer is not const (since your operator() is "mutable" instead of const).
    So you can modify the "n" member. */
    void operator()() {n = 20;}
};

class unnamed3
{
    int n;
public:
    unnamed3(int N) : n(N) {}

    /* BAD. Your this is const so you can't modify the "n" member. */
    void operator()() const {n = 10;}
};

int main()
{
    int n;
    unnamed1 u1(n); u1();    // OK
    unnamed2 u2(n); u2();    // OK
    //unnamed3 u3(n); u3();  // Error
    std::cout << n << "\n";  // "10"
}

ดังนั้นคุณอาจคิดว่า lambdas เป็นการสร้างคลาสที่มีโอเปอเรเตอร์ () ซึ่งเป็นค่าเริ่มต้นของ const เว้นแต่คุณจะบอกว่ามันไม่แน่นอน

นอกจากนี้คุณยังสามารถนึกถึงตัวแปรทั้งหมดที่จับภายใน [] (โดยชัดแจ้งหรือโดยนัย) ในฐานะสมาชิกของคลาสนั้น: สำเนาของวัตถุสำหรับ [=] หรืออ้างอิงถึงวัตถุสำหรับ [&] พวกเขาจะเริ่มต้นเมื่อคุณประกาศแลมบ์ดาของคุณราวกับว่ามีคอนสตรัคที่ซ่อนอยู่


5
ในขณะที่มีคำอธิบายที่ดีของสิ่งที่constหรือmutableแลมบ์ดาจะมีลักษณะเช่นถ้านำมาใช้เป็นเทียบเท่าประเภทที่ผู้ใช้กำหนดคำถามคือ (ในขณะที่ชื่อเรื่องและเนื้อหาโดย OP ในความคิดเห็น) ทำไม constเป็นค่าเริ่มต้นดังนั้นนี้ไม่ตอบมัน
underscore_d

36

ฉันรู้สึกว่าจุดจับภาพโดยรวมคือการอนุญาตให้ผู้ใช้เปลี่ยนชั่วคราว - ไม่เช่นนั้นฉันเกือบจะดีกว่าเสมอในการใช้การจับภาพอ้างอิงโดยอ้างอิงใช่ไหม?

คำถามคือมันคือ "เกือบ"? กรณีการใช้งานบ่อยครั้งดูเหมือนจะเป็นการส่งคืนหรือส่งผ่าน lambdas:

void registerCallback(std::function<void()> f) { /* ... */ }

void doSomething() {
  std::string name = receiveName();
  registerCallback([name]{ /* do something with name */ });
}

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


2
ตัวอย่างที่ดี นี่คือกรณีใช้ที่แข็งแกร่งมากสำหรับการใช้การดักจับโดยค่า แต่ทำไมไม่ได้เริ่มต้นที่จะconst? บรรลุวัตถุประสงค์อะไร? mutableดูเหมือนว่าออกจากสถานที่ที่นี่เมื่อconstเป็นไม่ได้เริ่มต้นใน "เกือบ" (ที่: P) ทุกอย่างอื่นของภาษา
kizzx2

8
@ kizzx2: ฉันหวังว่าจะconstเป็นค่าเริ่มต้นอย่างน้อยคนก็จะถูกบังคับให้พิจารณาความถูกต้องของ const: /
Matthieu M.

1
@ kizzx2 มองดูแลมบ์ดาเอกสารดูเหมือนว่าพวกเขาทำให้มันเป็นค่าเริ่มต้นเพื่อconstให้พวกเขาสามารถเรียกมันว่าวัตถุแลมบ์ดาเป็น const หรือไม่ std::function<void()> const&ยกตัวอย่างเช่นพวกเขาจะผ่านมันไปยังฟังก์ชั่นการ เพื่อให้แลมบ์ดาเปลี่ยนสำเนาที่บันทึกได้ในเอกสารเริ่มต้นสมาชิกข้อมูลของการปิดจะถูกกำหนดmutableภายในโดยอัตโนมัติ ตอนนี้คุณต้องใส่mutableแลมบ์ดาด้วยตนเอง ฉันไม่พบเหตุผลอย่างละเอียด
Johannes Schaub - litb

2
ดูopen-std.org/JTC1/SC22/WG21/docs/papers/2008/n2651.pdfสำหรับรายละเอียด
Johannes Schaub - litb

5
ณ จุดนี้สำหรับฉันคำตอบ / เหตุผล "จริง" น่าจะเป็น "พวกเขาล้มเหลวในการทำงานรายละเอียดการดำเนินการ": /
kizzx2

32

FWIW, Herb Sutter ซึ่งเป็นสมาชิกที่รู้จักกันดีของคณะกรรมการมาตรฐาน C ++ ให้คำตอบที่แตกต่างกันสำหรับคำถามนั้นในเรื่องความถูกต้องและการใช้งานของแลมบ์ดา :

ลองพิจารณาตัวอย่างมนุษย์ฟางนี้โดยที่โปรแกรมเมอร์จับตัวแปรโลคัลตามค่าและพยายามแก้ไขค่าที่จับ (ซึ่งเป็นตัวแปรสมาชิกของวัตถุแลมบ์ดา):

int val = 0;
auto x = [=](item e)            // look ma, [=] means explicit copy
            { use(e,++val); };  // error: count is const, need ‘mutable’
auto y = [val](item e)          // darnit, I really can’t get more explicit
            { use(e,++val); };  // same error: count is const, need ‘mutable’

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

บทความของเขาเกี่ยวกับสาเหตุที่ควรเปลี่ยนแปลงใน C ++ 14 มันสั้นเขียนดีคุ้มค่าการอ่านถ้าคุณต้องการทราบ "สิ่งที่อยู่ในใจ [คณะกรรมการสมาชิก] ใจ" เกี่ยวกับคุณลักษณะเฉพาะนี้


16

คุณต้องคิดว่าฟังก์ชั่นปิดของแลมบ์ดาเป็นอย่างไร ทุกครั้งที่คุณประกาศนิพจน์แลมบ์ดาคอมไพเลอร์จะสร้างประเภทการปิดซึ่งไม่มีอะไรน้อยไปกว่าการประกาศคลาสที่ไม่มีชื่อที่มีแอตทริบิวต์ ( สภาพแวดล้อมที่นิพจน์แลมบ์ดาที่ประกาศ) และการเรียก::operator()ใช้ฟังก์ชัน เมื่อคุณจับตัวแปรโดยใช้copy-by-valueคอมไพเลอร์จะสร้างแอconstททริบิวใหม่ในรูปแบบการปิดดังนั้นคุณจะไม่สามารถเปลี่ยนแปลงมันภายในนิพจน์แลมบ์ดาเพราะมันเป็นคุณสมบัติ "อ่านอย่างเดียว" นั่นคือเหตุผลที่พวกเขา เรียกว่า " การปิด " เพราะในบางกรณีคุณกำลังปิดนิพจน์แลมบ์ดาของคุณโดยการคัดลอกตัวแปรจากขอบเขตบนลงในขอบเขตแลมบ์ดาmutableเอนทิตีที่ถูกยึดจะกลายเป็นnon-constคุณลักษณะของประเภทการปิดของคุณ นี่คือสิ่งที่ทำให้การเปลี่ยนแปลงที่ทำในตัวแปรที่ไม่แน่นอนที่จับได้โดยค่าไม่ถูกส่งไปยังขอบเขตด้านบน แต่เก็บไว้ใน Lambda ที่เป็นสภาวะ พยายามจินตนาการว่ารูปแบบการปิดของลัมด้าที่เกิดขึ้นนั้นช่วยฉันได้มากและฉันหวังว่ามันจะช่วยคุณได้เช่นกัน


14

ดูร่างนี้ภายใต้ 5.1.2 [expr.prim.lambda], subclause 5:

ประเภทการปิดสำหรับแลมบ์ดานิพจน์มีตัวดำเนินการเรียกฟังก์ชั่นอินไลน์สาธารณะ (13.5.4) ซึ่งพารามิเตอร์และประเภทการส่งคืนถูกอธิบายโดยพารามิเตอร์การประกาศ - อนุประโยค clause และประเภท trailingreturn- ของแลมบ์ดา - นิพจน์ ผู้ประกอบการเรียกฟังก์ชั่นนี้จะประกาศ const (9.3.1) ถ้าและถ้าหากพารามิเตอร์-declaration-clause ของ lambdaexpression ไม่ได้ตามมาด้วยการเปลี่ยนแปลง

แก้ไขในความคิดเห็นของ litb: บางทีพวกเขาคิดว่าการจับภาพโดยค่าเพื่อให้การเปลี่ยนแปลงนอกตัวแปรไม่ได้สะท้อนภายในแลมบ์ดา? การอ้างอิงทำงานได้ทั้งสองวิธีดังนั้นนี่คือคำอธิบายของฉัน ไม่รู้ว่ามันดีไหม

แก้ไขในความคิดเห็นของ kizzx2: เวลาส่วนใหญ่ที่แลมบ์ดาถูกนำมาใช้เป็น functor สำหรับอัลกอริทึม constNess ที่เป็นค่าเริ่มต้นอนุญาตให้ใช้ในสภาพแวดล้อมคงที่เช่นเดียวกับconstฟังก์ชั่นปกติที่มีคุณสมบัติสามารถใช้งานได้ แต่constคุณสมบัติที่ไม่ผ่านการรับรองไม่สามารถทำได้ บางทีพวกเขาอาจคิดว่าทำให้ใช้งานง่ายขึ้นสำหรับกรณีเหล่านั้นผู้รู้ว่าเกิดอะไรขึ้นในใจ :)


เป็นมาตรฐาน แต่ทำไมพวกเขาถึงเขียนแบบนี้?
kizzx2

@ kizzx2: คำอธิบายของฉันอยู่ภายใต้คำพูดนั้นโดยตรง :) มันเกี่ยวข้องกับสิ่งที่ litb พูดเกี่ยวกับอายุการใช้งานของวัตถุที่ถูกจับ แต่ยังเพิ่มขึ้นอีกเล็กน้อย
Xeo

@Xeo: โอ้ใช่ฉันพลาดที่: P มันยังมีอีกดีคำอธิบายสำหรับการใช้งานที่ดีของการจับภาพโดยมีมูลค่า แต่ทำไมมันควรเป็นconstค่าเริ่มต้น? ผมมีอยู่แล้วสำเนาใหม่ก็ดูเหมือนว่าแปลกที่จะไม่ให้ฉันเปลี่ยนมัน - โดยเฉพาะอย่างยิ่งมันไม่ใช่สิ่งที่ผิดปกติโดยเฉพาะอย่างยิ่งกับมัน - mutableพวกเขาเพียงแค่ต้องการให้ฉันไปเพิ่ม
kizzx2

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

2
@ kizzx2 - หากเราสามารถเริ่มต้นใหม่อีกครั้งเราอาจมีvarคำหลักเพื่อให้การเปลี่ยนแปลงและคงที่เป็นค่าเริ่มต้นสำหรับทุกสิ่ง ตอนนี้เราทำไม่ได้เราต้องอยู่กับมัน IMO, C ++ 2011 ออกมาค่อนข้างดีเมื่อพิจารณาทุกอย่าง
โบเพอร์สัน

11

ฉันตกอยู่ภายใต้ความประทับใจที่จุดรวมของการดักจับโดยค่าคือการอนุญาตให้ผู้ใช้เปลี่ยนชั่วคราว - ไม่เช่นนั้นฉันเกือบจะดีกว่าการใช้การจับภาพอ้างอิงโดยอ้างอิงใช่ไหม?

nคือไม่ได้ชั่วคราว n เป็นสมาชิกของแลมบ์ดา - ฟังก์ชั่น - วัตถุที่คุณสร้างขึ้นด้วยนิพจน์แลมบ์ดา ความคาดหวังเริ่มต้นคือที่เรียกแลมบ์ดาของคุณไม่ได้ปรับเปลี่ยนสถานะของมันจึงเป็น const nเพื่อป้องกันไม่ให้คุณเผลอปรับเปลี่ยน


1
วัตถุแลมบ์ดาทั้งหมดนั้นเป็นวัตถุชั่วคราวสมาชิกก็มีช่วงเวลาชั่วคราวด้วยเช่นกัน
Ben Voigt

2
@Ben: IIRC ฉันหมายถึงปัญหาที่เมื่อมีคนพูดว่า "ชั่วคราว" ฉันเข้าใจว่ามันหมายถึงวัตถุชั่วคราวที่ไม่มีชื่อซึ่งแลมบ์ดาเอง แต่สมาชิกไม่ใช่ และจากแลมบ์ดา "ข้างใน" ก็ไม่สำคัญว่าตัวแลมบ์ดานั้นจะชั่วคราวหรือไม่ อ่านคำถามอีกครั้งแม้ว่ามันจะปรากฏว่า OP หมายถึงพูดคำว่า "n ข้างในแลมบ์ดา" เมื่อเขาพูดว่า "ชั่วคราว"
Martin Ba

6

คุณต้องเข้าใจความหมายของการจับ! มันจับไม่ผ่านการโต้แย้ง! ลองดูตัวอย่างรหัสบางส่วน:

int main()
{
    using namespace std;
    int x = 5;
    int y;
    auto lamb = [x]() {return x + 5; };

    y= lamb();
    cout << y<<","<< x << endl; //outputs 10,5
    x = 20;
    y = lamb();
    cout << y << "," << x << endl; //output 10,20

}

อย่างที่คุณเห็นแม้ว่าจะxถูกเปลี่ยน20เป็นแลมบ์ดาก็ยังคืนค่า 10 ( xยังคง5อยู่ในแลมบ์ดา) การเปลี่ยนแปลงxภายในแลมบ์ดาหมายความว่าการเปลี่ยนแลมบ์ดาเองในแต่ละครั้งที่โทร เพื่อบังคับใช้ความถูกต้องมาตรฐานแนะนำmutableคำสำคัญ โดยการระบุแลมบ์ดาว่าไม่แน่นอนคุณกำลังบอกว่าการโทรแลมด้าแต่ละครั้งอาจทำให้เกิดการเปลี่ยนแปลงแลมด้าเอง ลองดูตัวอย่างอื่น:

int main()
{
    using namespace std;
    int x = 5;
    int y;
    auto lamb = [x]() mutable {return x++ + 5; };

    y= lamb();
    cout << y<<","<< x << endl; //outputs 10,5
    x = 20;
    y = lamb();
    cout << y << "," << x << endl; //outputs 11,20

}

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


4

ขณะนี้มีข้อเสนอเพื่อบรรเทาความจำเป็นmutableในการประกาศแลมบ์ดา: n3424


มีข้อมูลเกี่ยวกับสิ่งที่มาจากไหน? โดยส่วนตัวฉันคิดว่ามันเป็นความคิดที่ไม่ดีเนื่องจากการ "จับการแสดงออกโดยพลการ" ใหม่ทำให้จุดปวดส่วนใหญ่ราบรื่น
Ben Voigt

1
@BenVoigt ใช่ดูเหมือนว่าการเปลี่ยนแปลงเพื่อประโยชน์ของการเปลี่ยนแปลง
Miles Rout

3
@ BenVoigt แม้ว่าจะยุติธรรม แต่ฉันคาดหวังว่าอาจมีนักพัฒนา C ++ หลายคนที่ไม่ทราบว่าmutableเป็นคำหลักใน C ++
Miles Rout

1

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

ในฟังก์ชั่นที่บริสุทธิ์เอาท์พุทขึ้นอยู่กับอินพุตเท่านั้นและไม่ได้อยู่ในสถานะภายในบางอย่าง ดังนั้นฟังก์ชั่นแลมบ์ดาถ้าบริสุทธิ์ไม่จำเป็นต้องเปลี่ยนสถานะของมันและดังนั้นจึงไม่เปลี่ยนรูป

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

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