เหตุใดโปรแกรมจึงใช้การปิด


58

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

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

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


7
"exlanation สุดท้ายของการปิดการตรวจสอบที่นี่" - คุณไม่มีลิงค์
Dan Pichelman

2
คุณควรให้ความกระจ่างเกี่ยวกับงานอะซิงโครนัสในความคิดเห็นใต้คำตอบที่เกี่ยวข้อง มันไม่ได้เป็นส่วนหนึ่งของคำถามเดิมของคุณและผู้ตอบจะไม่ได้รับแจ้งการแก้ไขของคุณ
Robert Harvey

5
เพียงชิ้นอาหารอันโอชะ: จุดสำคัญของการปิดคือการกำหนดขอบเขตศัพท์ (เมื่อเทียบกับการกำหนดขอบเขตแบบไดนามิก), การปิดโดยไม่ต้องกำหนดขอบเขตคำศัพท์จะไร้ประโยชน์ การปิดบางครั้งเรียกว่าการปิดคำศัพท์
Hoffmann

1
ปิดช่วยให้คุณสามารถยกตัวอย่างฟังก์ชั่น
user253751

1
อ่านหนังสือดีๆเกี่ยวกับการเขียนโปรแกรมฟังก์ชั่น บางทีอาจจะเริ่มต้นด้วยSICP
Basile Starynkevitch

คำตอบ:


33

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

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

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

// Return a list of all books with at least 'threshold' copies sold.
function bestSellingBooks(threshold) {
  return bookList.filter(
      function (book) { return book.sales >= threshold; }
    );
}

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

คุณสามารถเขียนได้โดยไม่ต้องปิด แต่จะต้องใช้รหัสมากขึ้นและยากที่จะติดตาม นอกจากนี้ JavaScript มีไวยากรณ์แลมบ์ดา verbose ค่อนข้าง ใน Scala ตัวอย่างเช่นร่างกายทั้งหมดจะเป็น:

bookList filter (_.sales >= threshold)

อย่างไรก็ตามหากคุณสามารถใช้ECMAScript 6ได้ด้วยฟังก์ชั่นลูกศรไขมันแม้กระทั่งรหัส JavaScript จะง่ายขึ้นมากและสามารถใส่ในบรรทัดเดียวได้

const bestSellingBooks = (threshold) => bookList.filter(book => book.sales >= threshold);

ในรหัสของคุณเองให้มองหาสถานที่ที่คุณสร้างหม้อไอน้ำจำนวนมากเพื่อสื่อสารค่าชั่วคราวจากที่หนึ่งไปอีกที่หนึ่ง นี่เป็นโอกาสที่ดีในการพิจารณาแทนที่ด้วยการปิด


วิธีปิดสนิทลด coupling อย่างไร
sbichenko

คุณจะต้องกำหนดข้อ จำกัด ทั้งbestSellingBooksรหัสและfilterรหัสเช่นอินเทอร์เฟซเฉพาะหรืออาร์กิวเมนต์ข้อมูลผู้ใช้เพื่อให้สามารถสื่อสารthresholdข้อมูลได้ ที่ผูกทั้งสองฟังก์ชั่นเข้าด้วยกันในรูปแบบที่ใช้ซ้ำได้น้อยลง
Karl Bielefeldt

51

โดยวิธีการอธิบายฉันจะยืมรหัสจากการโพสต์บล็อกที่ยอดเยี่ยมเกี่ยวกับการปิด มันเป็น JavaScript แต่เป็นภาษาที่มีการโพสต์บล็อกส่วนใหญ่ที่พูดถึงการปิดการใช้งานเพราะการปิดมีความสำคัญใน JavaScript

สมมติว่าคุณต้องการเรนเดอร์อาร์เรย์เป็นตาราง HTML คุณสามารถทำสิ่งนี้ได้:

function renderArrayAsHtmlTable (array) {
  var table = "<table>";
  for (var idx in array) {
    var object = array[idx];
    table += "<tr><td>" + object + "</td></tr>";
  }
  table += "</table>";
  return table;
}

แต่คุณอยู่ในความเมตตาของ JavaScript ว่าองค์ประกอบแต่ละอย่างในอาร์เรย์จะแสดงผลอย่างไร หากคุณต้องการควบคุมการเรนเดอร์คุณสามารถทำได้:

function renderArrayAsHtmlTable (array, renderer) {
  var table = "<table>";
  for (var idx in array) {
    var object = array[idx];
    table += "<tr><td>" + renderer(object) + "</td></tr>";
  }
  table += "</table>";
  return table;
}

และตอนนี้คุณสามารถผ่านฟังก์ชั่นที่คืนค่าการแสดงผลที่คุณต้องการ

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

function intTableWithTotals (intArray) {
  var total = 0;
  var renderInt = function (i) {
    total += i;
    return "Int: " + i + ", running total: " + total;
  };
  return renderObjectsInTable(intArray, renderInt);
}

ความมหัศจรรย์ที่เกิดขึ้นที่นี่ก็คือrenderInt การเข้าถึงการเข้าถึงtotalตัวแปรแม้ว่าจะrenderIntถูกเรียกและออกซ้ำ ๆ

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

อ่านเพิ่มเติม


10
โดยทั่วไปคุณสามารถพูดได้ว่า "ฟังก์ชั่นชั้นหนึ่ง == วัตถุที่มีเพียงวิธีเดียว", "ปิด == วัตถุที่มีเพียงวิธีเดียวและรัฐ", "วัตถุ == มัดของการปิด"
Jörg W Mittag

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

2
ที่จริงแล้วการพูดจาหยาบคายจริงๆ : การปิดคือสิ่งที่ทำให้จาวาสคริปต์เป็นวัตถุในตอนแรก! OO เป็นเรื่องเกี่ยวกับการลบข้อมูลและการปิดเป็นวิธีการดำเนินการลบข้อมูลใน JavaScript
Jörg W Mittag

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

@Giorgio: จริง ๆ แล้วมันเป็นวิธีการที่วัตถุถูกนำมาใช้ใน Scheme เช่นกันและในความเป็นจริงแล้วมันก็มีการใช้งานวัตถุอย่างไรใน JavaScript (ไม่น่าแปลกใจเมื่อพิจารณาถึงความสัมพันธ์ที่ใกล้ชิดกับ Scheme Scheme dialect และใช้ล่าม Scheme แบบฝังใน Netscape Navigator และหลังจากนั้นได้รับคำสั่งให้สร้างภาษา "กับวัตถุที่ดูเหมือน C ++" หลังจากนั้นเขาได้ทำการเปลี่ยนแปลงเล็กน้อยเพื่อให้สอดคล้องกับข้อกำหนดทางการตลาดเหล่านั้น)
Jörg W Mittag

22

วัตถุประสงค์ของการclosuresเป็นเพียงเพื่อรักษาสถานะ; ดังนั้นชื่อclosure- มันปิดเหนือรัฐ เพื่อความสะดวกในการอธิบายเพิ่มเติมฉันจะใช้ Javascript

โดยทั่วไปคุณมีฟังก์ชั่น

function sayHello(){
    var txt="Hello";
    return txt;
}

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

การปิดคือโครงสร้างของภาษาซึ่งอนุญาตให้ - ดังที่ได้กล่าวไว้ก่อนหน้า - เพื่อรักษาสถานะของตัวแปรและขยายขอบเขต

สิ่งนี้อาจมีประโยชน์ในหลายกรณี กรณีการใช้งานหนึ่งคือการก่อสร้างของฟังก์ชั่นการสั่งซื้อที่สูงขึ้น

ในวิชาคณิตศาสตร์และวิทยาการคอมพิวเตอร์ฟังก์ชันลำดับสูงกว่า (เช่นรูปแบบการใช้งานหน้าที่หรือ functor) เป็นฟังก์ชันที่ทำอย่างน้อยหนึ่งอย่างต่อไปนี้: 1

  • รับฟังก์ชั่นหนึ่งอย่างหรือมากกว่านั้นเป็นอินพุต
  • ส่งออกฟังก์ชั่น

ตัวอย่างที่เรียบง่าย แต่ไม่ได้มีประโยชน์มากเกินไปคือ:

 makeadder=function(a){
     return function(b){
         return a+b;
     }
 }

 add5=makeadder(5);
 console.log(add5(10)); 

คุณกำหนดฟังก์ชั่นmakedadderซึ่งจะใช้เวลาหนึ่งพารามิเตอร์เป็น input และผลตอบแทนการทำงาน มีความเป็นนอกฟังก์ชั่นfunction(a){}และภายใน function(b){}{} .Further คุณกำหนด (implicitely) ฟังก์ชั่นอื่นadd5เป็นผลมาจากการเรียก makeadderfuntion makeadder(5)ส่งคืนฟังก์ชั่นไม่ระบุชื่อ ( ภายใน ) ซึ่งจะใช้เวลา 1 พารามิเตอร์และส่งกลับผลรวมของพารามิเตอร์ของฟังก์ชั่นด้านนอกและพารามิเตอร์ของฟังก์ชั่นภายใน

เคล็ดลับคือว่าในขณะที่กลับมาภายในฟังก์ชั่นที่ไม่เกิดขึ้นจริงเพิ่มขอบเขตของพารามิเตอร์ของฟังก์ชั่นด้านนอก (คนa) จะถูกเก็บรักษาไว้ add5 จำได้ว่าพารามิเตอร์เป็นa5

หรือเพื่อแสดงตัวอย่างที่มีประโยชน์อย่างน้อยหนึ่งตัวอย่าง:

  makeTag=function(openTag, closeTag){
     return function(content){
         return openTag +content +closeTag;
     }
 }

 table=makeTag("<table>","</table>")
 tr=makeTag("<tr>", "</tr>");
 td=makeTag("<td>","</td>");
 console.log(table(tr(td("I am a Row"))));

อีก usecase ทั่วไปคือIIFE = เรียกใช้การแสดงออกของฟังก์ชั่นทันที มันเป็นเรื่องธรรมดามากในจาวาสคริปต์เพื่อปลอมตัวแปรสมาชิกส่วนตัว สิ่งนี้ทำผ่านฟังก์ชั่นซึ่งสร้างprivate scope = closureเนื่องจากเป็นทันทีหลังจากการเรียกใช้นิยาม function(){}()โครงสร้าง สังเกตเครื่องหมายวงเล็บ()หลังคำจำกัดความ นี้จะทำให้มันเป็นไปได้ที่จะใช้สำหรับการสร้างวัตถุที่มีการเปิดเผยรูปแบบโมดูล เคล็ดลับคือการสร้างขอบเขตและส่งคืนวัตถุซึ่งสามารถเข้าถึงขอบเขตนี้ได้หลังจากการดำเนินการของ IIFE

ตัวอย่างของ Addi มีลักษณะเช่นนี้:

 var myRevealingModule = (function () {

         var privateVar = "Ben Cherry",
             publicVar = "Hey there!";

         function privateFunction() {
             console.log( "Name:" + privateVar );
         }

         function publicSetName( strName ) {
             privateVar = strName;
         }

         function publicGetName() {
             privateFunction();
         }


         // Reveal public pointers to
         // private functions and properties

         return {
             setName: publicSetName,
             greeting: publicVar,
             getName: publicGetName
         };

     })();

 myRevealingModule.setName( "Paul Kinlan" );

วัตถุกลับมีการอ้างอิงถึงฟังก์ชั่น (เช่นpublicSetName) privateVarซึ่งจะมีการเข้าถึงตัวแปร

แต่นี่เป็นกรณีการใช้งานพิเศษสำหรับ Javascript

โปรแกรมเมอร์คนใดที่จะทำหน้าที่เฉพาะเจาะจงซึ่งอาจได้รับผลงานที่ดีที่สุดโดยการปิดงาน?

มีสาเหตุหลายประการ หนึ่งอาจจะเป็นไปได้ว่ามันเป็นธรรมชาติสำหรับเขาเพราะเขาเป็นไปตามกระบวนทัศน์การทำงาน หรือใน Javascript: มันเป็นเพียงความจำเป็นต้องพึ่งพาการปิดเพื่อหลีกเลี่ยงนิสัยใจคอของภาษา


"วัตถุประสงค์ของการปิดเป็นเพียงการรักษาสถานะดังนั้นการปิดชื่อ - มันปิดเหนือรัฐ": ขึ้นอยู่กับภาษาจริงๆ การปิดอย่างใกล้ชิดกับชื่อ / ตัวแปรภายนอก สิ่งเหล่านี้อาจแสดงสถานะ (ตำแหน่งหน่วยความจำที่สามารถเปลี่ยนแปลงได้) แต่อาจหมายถึงค่า (ไม่เปลี่ยนรูป) การปิดประตูใน Haskell ไม่รักษาสถานะ: พวกเขาเก็บรักษาข้อมูลที่เป็นที่รู้จักในขณะนี้และในบริบทที่การปิดถูกสร้างขึ้น
Giorgio

1
»พวกเขารักษาข้อมูลที่เป็นที่รู้จักในขณะนี้และในบริบทที่ปิดที่ถูกสร้างขึ้น«อิสระไม่ว่าจะอาจมีการเปลี่ยนแปลงหรือไม่ก็เป็นรัฐ - บางทีรัฐไม่เปลี่ยนรูป
Thomas Junk

16

มีสองกรณีการใช้งานหลักสำหรับการปิด:

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

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

มีการใช้งานอื่นหลายอย่างสำหรับการปิด แต่สิ่งเหล่านั้นเป็นสองวิธีหลัก


23
ดังนั้นโดยทั่วไปแล้วการเรียกกลับเนื่องจากเป็นตัวอย่างแรกคือการติดต่อกลับ
Robert Harvey

2
@ RobertHarvey: จริงทางเทคนิค แต่พวกเขาเป็นแบบจำลองทางจิตที่แตกต่างกัน ตัวอย่างเช่น (โดยทั่วไปแล้ว) คุณคาดหวังว่าตัวจัดการเหตุการณ์จะถูกเรียกหลายครั้ง แต่การเรียกใช้การซิงค์แบบ async ของคุณจะถูกเรียกเพียงครั้งเดียวเท่านั้น แต่ใช่ในทางเทคนิคสิ่งที่คุณทำกับการปิดคือการติดต่อกลับ (เว้นแต่คุณจะแปลงเป็นต้นไม้แสดงอารมณ์ แต่นั่นเป็นเรื่องที่แตกต่างออกไปอย่างสิ้นเชิง);)
Mason Wheeler

4
@RobertHarvey: การดูการปิดในขณะที่การโทรกลับทำให้คุณอยู่ในกรอบความคิดที่จะทำให้คุณไม่สามารถใช้งานได้อย่างมีประสิทธิภาพ ปิดเป็นโทรกลับในเตียรอยด์
gnasher729

13
@MasonWheeler พูดแบบนี้มันฟังดูผิด การโทรกลับไม่มีส่วนเกี่ยวข้องกับการปิด แน่นอนคุณสามารถโทรกลับฟังก์ชั่นภายในปิดหรือโทรปิดเป็นโทรกลับ; แต่การโทรกลับไม่จำเป็นต้องปิด การเรียกกลับเป็นเพียงการเรียกใช้ฟังก์ชันอย่างง่าย Eventhandler ไม่ใช่การปิด ผู้รับมอบสิทธิ์ไม่จำเป็นต้องทำการปิดบัญชี: โดยส่วนใหญ่แล้วจะเป็นวิธีพอยน์เตอร์ของฟังก์ชัน C # แน่นอนคุณสามารถใช้มันเพื่อสร้างการปิด คำอธิบายของคุณไม่แน่ชัด จุดกำลังปิดเหนือรัฐและใช้ประโยชน์จากมัน
Thomas Junk

5
อาจมีความหมายเฉพาะของ JS ที่เกิดขึ้นที่นี่ซึ่งทำให้ฉันเข้าใจผิด แต่คำตอบนี้ฟังดูผิดสำหรับฉัน Asynchrony หรือ callbacks สามารถใช้อะไรก็ได้ที่ 'callable' การปิดไม่จำเป็นสำหรับทั้งสองอย่าง - ฟังก์ชั่นปกติจะทำได้ดี ในขณะเดียวกันการปิดตัวตามที่กำหนดไว้ในฟังก์ชันการเขียนโปรแกรมหรือใช้ใน Python นั้นมีประโยชน์อย่างที่ Thomas กล่าวเพราะมัน 'ปิด' บางสถานะเช่นตัวแปรและให้ฟังก์ชันในขอบเขตภายในที่สอดคล้องกับการเข้าถึงของตัวแปรนั้น ค่าแม้ว่าฟังก์ชันถูกเรียกใช้และออกหลายครั้ง
Jonathan Hartley

13

ตัวอย่างอื่น ๆ :

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

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

วิธีการนี้ทำให้ไม่สามารถมีหลายเธรดพร้อมกันเรียงลำดับรายชื่อเมืองเดียวกันตามระยะทางไปยังสองสถานที่ต่างกัน การปิดที่ล้อมรอบสถานที่แก้ปัญหานี้และกำจัดความต้องการตัวแปรส่วนกลาง


ตัวเลขสุ่ม
ต้นฉบับrand()ไม่มีข้อโต้แย้ง เครื่องกำเนิดไฟฟ้าจำนวนสุ่มเทียมต้องมีสถานะ บางคน (เช่น Mersenne Twister) ต้องการสถานะมากมาย แม้เรียบง่าย แต่น่ากลัวrand()รัฐจำเป็น อ่านบทความวารสารคณิตศาสตร์บนเครื่องกำเนิดตัวเลขสุ่มตัวใหม่และคุณจะเห็นตัวแปรทั่วโลกอย่างหลีกเลี่ยงไม่ได้ นั่นเป็นสิ่งที่ดีสำหรับนักพัฒนาของเทคนิคไม่ใช่สิ่งที่ดีสำหรับผู้โทร การห่อหุ้มสถานะนั้นในโครงสร้างและส่งผ่านโครงสร้างไปยังตัวสร้างตัวเลขสุ่มเป็นวิธีหนึ่งในการแก้ไขปัญหาข้อมูลทั่วโลก นี่เป็นวิธีการที่ใช้ในหลายภาษาที่ไม่ใช่ OO เพื่อสร้าง reentrant ตัวสร้างตัวเลขแบบสุ่ม การปิดซ่อนสถานะนั้นจากผู้โทร การปิดเป็นการเสนอลำดับการโทรอย่างง่ายrand()และการแสดงความซ้ำซ้อนของสถานะที่ห่อหุ้ม

มีจำนวนสุ่มมากกว่าแค่ PRNG คนส่วนใหญ่ที่ต้องการความสุ่มต้องการกระจายมันอย่างแน่นอน ฉันจะเริ่มต้นด้วยตัวเลขสุ่มจาก 0 ถึง 1 หรือ U (0,1) สำหรับช่วงสั้น ๆ PRNG ใด ๆ ที่สร้างจำนวนเต็มตั้งแต่ 0 ถึงสูงสุดจะทำ เพียงแค่หาร (เป็นทศนิยม) จำนวนเต็มแบบสุ่มด้วยจำนวนสูงสุด วิธีที่สะดวกและทั่วไปในการดำเนินการนี้คือการสร้างการปิดที่ใช้การปิด (PRNG) และค่าสูงสุดเป็นอินพุต ตอนนี้เรามีตัวสร้างแบบสุ่มทั่วไปและใช้งานง่ายสำหรับ U (0,1)

มีการแจกแจงอื่น ๆ อีกมากมายนอกเหนือจาก U (0,1) ตัวอย่างเช่นการแจกแจงแบบปกติที่มีค่าเฉลี่ยและค่าเบี่ยงเบนมาตรฐาน อัลกอริธึมตัวสร้างการแจกแจงปกติทุกตัวที่ฉันเรียกใช้ใช้ตัวสร้างตัว U วิธีที่สะดวกและทั่วไปในการสร้างตัวสร้างแบบปกติคือการสร้างตัวปิดที่หุ้มตัวสร้าง U (0,1) ค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานเป็นสถานะ นี่คือแนวคิดอย่างน้อยการปิดที่ใช้การปิดที่ใช้การปิดเป็นอาร์กิวเมนต์


7

การปิดจะเทียบเท่ากับวัตถุที่ใช้วิธีการ run () และในทางกลับกันวัตถุสามารถเลียนแบบได้ด้วยการปิด

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

  • ข้อดีของวัตถุคือความเป็นไปได้ที่จะมีปฏิสัมพันธ์ที่ซับซ้อนมากขึ้น: วิธีการหลายวิธีและ / หรืออินเทอร์เฟซที่แตกต่างกัน

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

 (let ((seen))
    (defun register-name (name)
       (pushnew name seen :test #'string=))

    (defun all-names ()
       (copy-seq seen))

    (defun reset-name-registry ()
       (setf seen nil)))

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


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

อย่างไรก็ตามมันค่อนข้างง่ายในการป้องกันการใช้ในทางที่ผิดดังกล่าวโดยการจับตัวแปรควบคุมที่จะป้องกันการดำเนินการปิด แม่นยำมากขึ้นนี่คือสิ่งที่ฉันมีอยู่ในใจ (ใน Common LISP):

(defun guarded (function)
  (let ((active t))
    (values (lambda (&rest args)
              (when active
                (apply function args)))
            (lambda ()
              (setf active nil)))))

ที่นี่เรารับผู้ออกแบบฟังก์ชั่นfunctionและกลับมาปิดสองครั้งโดยทั้งคู่จับตัวแปรท้องถิ่นชื่อactive:

  • คนแรกที่ได้รับมอบหมายfunctionเท่านั้นเมื่อactiveเป็นจริง
  • คนที่สองชุดactionเพื่ออาคาnilfalse

แทนที่จะ(when active ...)เป็นไปได้ที่จะมีการ(assert active)แสดงออกซึ่งอาจทำให้เกิดข้อยกเว้นในกรณีที่มีการปิดการเรียกเมื่อไม่ควร นอกจากนี้โปรดทราบว่ารหัสที่ไม่ปลอดภัยอาจมีข้อยกเว้นเกิดขึ้นเองเมื่อใช้งานไม่ดีดังนั้นคุณจึงจำเป็นต้องใช้เครื่องมือห่อหุ้ม

นี่คือวิธีที่คุณจะใช้:

(use-package :metabang-bind) ;; for bind

(defun example (obj1 obj2)
  (bind (((:values f f-deactivator)(guarded (lambda () (do-stuff obj1))))
         ((:values g g-deactivator)(guarded (lambda () (do-thing obj2)))))

    ;; ensure the closure are inactive when we exit
    (unwind-protect
         ;; pass closures to other functions
         (progn
           (do-work f)
           (do-work g))

      ;; cleanup code: deactivate closures
      (funcall f-deactivator)
      (funcall g-deactivator))))

โปรดทราบว่าสามารถปิดการปิดใช้งาน desactivating ให้กับฟังก์ชันอื่นเช่นกัน ที่นี่activeตัวแปรท้องถิ่นจะไม่ถูกแชร์ระหว่างfและg; นอกจากนี้ยังมีนอกเหนือไปactive, fเพียง แต่หมายถึงobj1และgเพียง obj2แต่หมายถึง

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


1
ข้อเสียของการปิดตามที่นำมาใช้ทั่วไปคือเมื่อการปิดที่ใช้หนึ่งในตัวแปรท้องถิ่นของวิธีการสัมผัสกับโลกภายนอกวิธีการกำหนดปิดอาจสูญเสียการควบคุมเมื่ออ่านและเขียนตัวแปรนั้น ในภาษาส่วนใหญ่ไม่มีวิธีที่สะดวกในการกำหนดการปิดชั่วคราวส่งต่อไปยังวิธีการและรับประกันว่าจะหยุดอยู่เมื่อวิธีที่ได้รับกลับคืนมาและไม่มีวิธีใดในภาษาแบบมัลติเธรดเพื่อ รับประกันว่าการปิดจะไม่ทำงานในบริบทเธรดที่ไม่คาดคิด
supercat

@supercat: ดูที่ Objective-C และ Swift
gnasher729

1
@supercat จะไม่มีข้อเสียแบบเดียวกันกับวัตถุที่เป็นของรัฐหรือไม่
Andres F.

@AndresF: เป็นไปได้ที่จะแค็ปซูลวัตถุ stateful ในเสื้อคลุมที่รองรับการทำให้เป็นโมฆะ; หากรหัสสุนทรีย์เอกชนList<T>ใน (ชั้นสมมุติ) และตีแผ่ว่ารหัสข้างนอกก็อาจจะทำให้มั่นใจได้ว่าถ้ามันเลิกเสื้อคลุมรหัสข้างนอกไม่ได้จะมีวิธีการจัดการใดTemporaryMutableListWrapper<T>List<T>หนึ่งสามารถออกแบบการปิดเพื่ออนุญาตให้ใช้งานไม่ได้เมื่อพวกเขาได้ทำตามวัตถุประสงค์ที่คาดไว้ แต่ก็ไม่สะดวก มีการปิดเพื่อให้รูปแบบบางอย่างสะดวกสบายและความพยายามที่จำเป็นในการป้องกันพวกเขาจะคัดค้านสิ่งนั้น
supercat

1
@coredump: ใน C # หากการปิดทั้งสองมีตัวแปรใด ๆ ร่วมกันวัตถุที่สร้างโดยคอมไพเลอร์เดียวกันจะให้บริการทั้งคู่เนื่องจากการเปลี่ยนแปลงที่เกิดขึ้นกับตัวแปรที่ใช้ร่วมกันโดยการปิดหนึ่งครั้งจะต้องเห็นโดยผู้อื่น การหลีกเลี่ยงการแบ่งปันนั้นจำเป็นต้องให้แต่ละการปิดเป็นวัตถุของตัวเองซึ่งมีตัวแปรที่ไม่แชร์ของตัวเองรวมถึงการอ้างอิงไปยังวัตถุที่ใช้ร่วมกันซึ่งมีตัวแปรที่ใช้ร่วมกัน ไม่เป็นไปไม่ได้ แต่มันจะเพิ่มระดับพิเศษของการยกเลิกการเข้าถึงตัวแปรส่วนใหญ่ทำให้ทุกอย่างช้าลง
supercat

6

ไม่มีอะไรที่ยังไม่ได้พูด แต่อาจเป็นตัวอย่างที่ง่ายกว่า

นี่คือตัวอย่าง JavaScript โดยใช้การหมดเวลา:

// Example function that logs something to the browser's console after a given delay
function delayedLog(message, delay) {
  // this function will be called when the timer runs out
  var fire = function () {
    console.log(message); // closure magic!
  };

  // set a timeout that'll call fire() after a delay
  setTimeout(fire, delay);
}

สิ่งที่เกิดขึ้นที่นี่คือเมื่อdelayedLog()ถูกเรียกมันจะกลับมาทันทีหลังจากตั้งค่าการหมดเวลาและการหมดเวลาจะดำเนินการฟ้องในพื้นหลัง

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

แต่ลองจินตนาการว่า JavaScript ไม่มีการปิด

วิธีหนึ่งที่จะทำให้setTimeout()การบล็อกเหมือนฟังก์ชั่น "สลีป" ดังนั้นdelayedLog()ขอบเขตของมันจะไม่หายไปจนกว่าจะหมดเวลา แต่การปิดกั้นทุกอย่างไม่ค่อยดีนัก

อีกวิธีหนึ่งคือการวางmessageตัวแปรในขอบเขตอื่นที่จะสามารถเข้าถึงได้หลังจากdelayedLog()ขอบเขตของหายไป

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

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

ดังนั้น ... ใช่นี่มันช่างน่าเบื่อ

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


ฉันมีปัญหาเรียกว่าเป็นปิด บางทีผมอาจจะเหรียญมันปิดไม่ได้ตั้งใจ messageถูกล้อมรอบในขอบเขตของฟังก์ชันfireและดังนั้นจึงถูกอ้างถึงในการโทรเพิ่มเติม แต่มันไม่ได้ตั้งใจว่าจะพูด ในทางเทคนิคมันเป็นการปิด +1 ต่อไป;)
Thomas Junk

@ThomasJunk ฉันไม่แน่ใจว่าฉันค่อนข้างติดตาม ลักษณะ " การปิดโดยไม่บังเอิญ" จะมีลักษณะอย่างไร ฉันเห็นmakeadderตัวอย่างของคุณด้านบนซึ่งในสายตาของฉันจะปรากฏมากเหมือนกัน คุณส่งคืนฟังก์ชัน "curried" ที่ใช้อาร์กิวเมนต์เดียวแทนที่จะเป็นสองฟังก์ชัน โดยใช้วิธีการเดียวกันฉันสร้างฟังก์ชั่นที่ใช้ศูนย์อาร์กิวเมนต์ ฉันแค่ไม่ส่งคืน แต่ส่งต่อให้setTimeoutแทน
Flambino

»ฉันแค่ไม่ส่งคืน«บางทีนั่นคือประเด็นซึ่งสร้างความแตกต่างให้ฉัน ฉันไม่สามารถพูดได้ชัดเจนว่า "ความกังวล" ของฉัน;) ในแง่เทคนิคคุณมีสิทธิ์ 100% การอ้างอิงmessageในfireสร้างการปิด และเมื่อมีการเรียกใช้setTimeoutมันจะใช้ประโยชน์จากสถานะที่สงวนไว้
โทมัสขยะ

1
@ ThomasJunk ฉันเห็นว่ามันอาจมีกลิ่นแตกต่างกันเล็กน้อย ฉันไม่อาจแบ่งปันความกังวลของคุณได้ :) หรือไม่ว่าในกรณีใดฉันจะไม่เรียกมันว่า "บังเอิญ" - ค่อนข้างแน่ใจว่าฉันเขียนมันในแบบที่ตั้งใจ;)
Flambino

3

PHPสามารถใช้เพื่อช่วยแสดงตัวอย่างจริงในภาษาอื่น

protected function registerRoutes($dic)
{
  $router = $dic['router'];

  $router->map(['GET','OPTIONS'],'/api/users',function($request,$response) use ($dic)
  {
    $controller = $dic['user_api_controller'];
    return $controller->findAllAction($request,$response);
  })->setName('api_users');
}

ดังนั้นโดยทั่วไปฉันกำลังลงทะเบียนฟังก์ชั่นซึ่งจะมีการดำเนินการสำหรับ / API / ผู้ใช้URI นี่เป็นฟังก์ชั่นมิดเดิลแวร์ที่เก็บไว้ในสแต็ก ฟังก์ชั่นอื่น ๆ จะถูกพันไว้รอบ ๆ สวยมากเช่นNode.js / Express.jsไม่

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


-1

การปิดเป็นส่วนหนึ่งของรหัสโดยพลการรวมถึงตัวแปรที่สามารถจัดการได้เป็นข้อมูลชั้นหนึ่ง

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

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

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

มัลติเธรด: iOS / MacOS X มีฟังก์ชั่นในการทำสิ่งต่าง ๆ เช่น "ทำการปิดกระทู้นี้บนพื้นหลัง", "... ในเธรดหลัก", "... ในเธรดหลัก, 10 วินาทีจากนี้" มันทำให้ multithreading จิ๊บจ๊อย

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

นั่นเป็นจุดเริ่มต้นเล็ก ๆ ห้าสถานการณ์ที่การปิดทำการปฏิวัติในแง่ของการสร้างรหัสที่กะทัดรัดอ่านง่ายเชื่อถือได้และมีประสิทธิภาพ


-4

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

ข้างต้นเป็นบทสรุปของการทำความเข้าใจแลมบ์ดานิพจน์โดย Dan Avidar

สิ่งนี้ทำให้การใช้งาน closures สำหรับฉันชัดเจนขึ้นเพราะมันช่วยอธิบายทางเลือก (การปิด vs method) และประโยชน์ของแต่ละอัน

รหัสต่อไปนี้ใช้เพียงครั้งเดียวและครั้งเดียวเท่านั้นระหว่างการตั้งค่า การเขียนไว้ในตำแหน่งภายใต้ viewDidLoad จะบันทึกปัญหาในการค้นหาที่อื่นและย่อขนาดของรหัสให้สั้นลง

myPhoton!.getVariable("Temp", completion: { (result:AnyObject!, error:NSError!) -> Void in
  if let e = error {
    self.getTempLabel.text = "Failed reading temp"
  } else {
    if let res = result as? Float {
    self.getTempLabel.text = "Temperature is \(res) degrees"
    }
  }
})

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

ปิดอีก; อันนี้เก็บค่า ...

let animals = ["fish", "cat", "chicken", "dog"]
let sortedStrings = animals.sorted({ (one: String, two: String) -> Bool in return one > two
}) println(sortedStrings)

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

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

1
อาจเป็นคำตอบที่ไม่อันตรายแต่แน่นอนว่า "ชัดเจน [... ] ไม่ถูกต้อง" มันใช้คำที่ไม่ถูกต้องสำหรับแนวคิดที่ประกอบกันอย่างมากและมักจะใช้ร่วมกัน - ตรงที่ความชัดเจนของความแตกต่างเป็นสิ่งจำเป็น นี่อาจเป็นสาเหตุของปัญหาการออกแบบสำหรับโปรแกรมเมอร์ที่อ่านข้อความนี้หากไม่ได้รับการแก้ไข (และเนื่องจากเท่าที่ฉันสามารถบอกได้ตัวอย่างรหัสก็ไม่ได้มีการปิดใด ๆ เพียงฟังก์ชั่นแลมบ์ดาก็ไม่ได้ช่วยอธิบายว่าทำไมการปิดจะเป็นประโยชน์)
นาธาน Tuggy

นาธานตัวอย่างรหัสนั้นเป็นการปิดใน Swift คุณสามารถถามคนอื่นถ้าคุณสงสัยฉัน ดังนั้นถ้าเป็นการปิดคุณจะโหวตหรือไม่
Bendrix

1
แอปเปิ้ลอย่างเป็นธรรมอย่างชัดเจนอธิบายสิ่งที่พวกเขาหมายถึงโดยปิดในเอกสารของพวกเขา "Closures เป็นบล็อกที่มีฟังก์ชั่นในตัวที่สามารถส่งผ่านและใช้ในโค้ดของคุณได้การปิดใน Swift นั้นคล้ายคลึงกับบล็อกใน C และ Objective-C และ lambdas ในภาษาโปรแกรมอื่น ๆ " เมื่อคุณได้รับการดำเนินการและคำศัพท์มันอาจสร้างความสับสน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.