سؤال

لقد وجدت أن الكثير من الناس يستخدمون الكلمات إنهاء و حاجز بالتبادل.معظم هؤلاء الأشخاص لا يستطيعون شرح ما يتحدثون عنه.

يتحدث بعض مبرمجي Java (حتى أولئك الذين يعملون في شركات استشارية باهظة الثمن) عن الفئات الداخلية المجهولة على أنها "كتل" و"إغلاقات" - لكنني أعلم أن هذا ليس صحيحًا.(لا يمكنك تمرير المتغيرات القابلة للتغيير من نطاق الطريقة التي تم تعريفها بها...)

أبحث عن:

  • علوم الكمبيوتر الدقيقة تعريف الكتلة
  • علوم الكمبيوتر الدقيقة تعريف الإغلاق
  • و توضيح بشأن الفرق بين الاثنين.

أود حقًا رؤية الروابط أو المقالات أو الكتب مراجع حول هذه من فضلك.

هل كانت مفيدة؟

المحلول

بينما أ حاجز هو مجرد جزء من التعليمات البرمجية التي يمكن أن تتكون من البيانات والإعلانات ولكن لا شيء آخر، أ إنهاء هو كائن حقيقي من الدرجة الأولى، وهو متغير حقيقي له كتلة كقيمة له.

والفرق الرئيسي هو أن الكتلة تقوم ببساطة بتجميع التعليمات معًا (على سبيل المثال نص ملف بينما بيان)، في حين أن الإغلاق هو متغير يحتوي على بعض التعليمات البرمجية التي يمكن تنفيذها.

إذا كان لديك إغلاق، فعادةً ما يمكنك تمريره كمعلمة إلى الوظائف، وتقليصه وتقليصه، واستدعاءه بشكل أساسي!

Closure c = { println 'Hello!' }
/* now you have an object that contains code */
c.call()

بالطبع عمليات الإغلاق أكثر قوة، فهي متغيرات ويمكن استخدامها لتحديد السلوك المخصص للكائنات (بينما يتعين عليك عادةً استخدام واجهات أو أساليب OOP الأخرى في البرمجة).

يمكنك التفكير في أ إنهاء كدالة تحتوي على ما تفعله تلك الوظيفة داخل نفسها.

تعتبر الكتل مفيدة لأنها تسمح بتحديد نطاق المتغيرات.عادةً عندما تقوم بتعريف متغير داخل نطاق ما، يمكنك تجاوز التعريفات الخارجية دون أي مشاكل وستوجد تعريفات جديدة أثناء تنفيذ الكتلة فقط.

for (int i = 0; i < 10; ++i)
{
     int t = i*2;
     printf("%d\r\n", t);
}

t يتم تعريفه داخل الكتلة (جسم for بيان) وسوف تستمر فقط داخل تلك الكتلة.

نصائح أخرى

الكتلة هي شيء نحوي - وحدة منطقية من البيانات (أكثر ارتباطًا بـ نِطَاق من ل إنهاء).

if (Condition) {
    // Block here 
} 
else {
    // Another block
}

يرتبط الإغلاق بوظائف أو فئات مجهولة - كائن (وظيفة) مجهول، وهو جزء من التعليمات البرمجية المرتبطة ببيئة (مع متغيراتها).

def foo() {
   var x = 0
   return () => { x += 1; return x }
}

هنا foo يعود الإغلاق!المتغير المحلي x يستمر خلال الإغلاق حتى بعد ذلك foo تم إنهاؤه ويمكن زيادته من خلال استدعاءات الوظيفة المجهولة التي تم إرجاعها.

val counter = foo()
print counter() // Returns 2
print counter() // Return 3

لاحظ أن روبي هي الوحيدة التي يتم فيها التعامل مع الحظر والإغلاق بشكل مشابه لما تسميه روبي الحظر هو إنهاء:

(1..10).each do |x|
    p x
end

هناك each- يتم تمرير الطريقة إلى وظيفة إغلاق (أخذ معلمة x) والتي تسمى a حاجز في روبي.

هناك الكثير من الالتباس هنا لأن هناك مصطلحات ذات تعريفات متعددة، والعديد من الأشياء المميزة التي يتم الخلط بينها ببساطة لأنها عادةً ما توجد معًا.

أولا، لدينا "بلوك".هذا مجرد جزء معجمي من التعليمات البرمجية التي تشكل وحدة - جسم الحلقة، على سبيل المثال.إذا كانت اللغة تحتوي بالفعل على نطاق كتلة، فيمكن تعريف المتغيرات الموجودة فقط ضمن هذا الجزء من التعليمات البرمجية.

ثانيًا، لدينا كود قابل للاستدعاء كنوع قيمة.في اللغات الوظيفية، هذه هي قيم الوظائف - تسمى أحيانًا "funs"، "وظائف مجهولة" (لأن الوظيفة موجودة في القيمة، وليس في الاسم المخصص لها؛لا تحتاج إلى اسم لتسميتها)، أو "lambdas" (من عامل التشغيل المستخدم لإنشائها في حساب التفاضل والتكامل Lambda الخاص بالكنيسة).قد يطلق عليها اسم "عمليات الإغلاق"، ولكنها ليست عمليات إغلاق حقيقية تلقائيًا؛من أجل التأهل، يجب عليهم تغليف ("إغلاق") النطاق المعجمي المحيط بإنشائهم - أي أن المتغيرات المحددة خارج نطاق الوظيفة نفسها ولكن ضمن نطاق تعريفها لا تزال متاحة عندما يتم استدعاء الوظيفة، حتى إذا كانت نقطة الاتصال بعد أن يكون المتغير المشار إليه قد خرج عن النطاق وتم إعادة تدوير تخزينه.

على سبيل المثال، خذ بعين الاعتبار جافا سكريبت هذه:

function makeClosure() {
  var x = "Remember me!";
  return function() {
    return "x='" + x + "'";
  }
}

// console.log(x); 
// The above is an error; x is undefined
var f = makeClosure();
console.log(f());
// The above outputs a string that includes x as it existed when f was created.

المتغير x يتم تعريفه فقط داخل نص الوظيفة makeClosure;خارج هذا التعريف، فهو غير موجود.بعد أن نتصل makeClosure, ، ال x أعلن داخله يجب أن يكون قد ذهب.وهو كذلك، من وجهة نظر معظم التعليمات البرمجية.ولكن تم إرجاع الدالة بواسطة makeClosure وأعلن في حين x كان موجودًا، لذلك لا يزال بإمكانه الوصول إليه عند الاتصال به لاحقًا.وهذا يجعلها إغلاقًا حقيقيًا.

يمكن أن يكون لديك قيم دالة ليست عمليات إغلاق، لأنها لا تحافظ على النطاق.يمكنك أيضًا إجراء عمليات إغلاق جزئية؛تحافظ قيم دالة PHP فقط على متغيرات محددة يجب إدراجها في وقت إنشاء القيمة.

يمكنك أيضًا الحصول على قيم تعليمات برمجية قابلة للاستدعاء لا تمثل وظائف كاملة على الإطلاق.يُطلق Smalltalk على هذه "إغلاقات الكتل"، بينما يطلق عليها روبي "procs"، على الرغم من أن العديد من مستخدمي Ruby يطلقون عليها فقط "كتل"، لأنها النسخة المُجسدة لما تم إنشاؤه بواسطة {...} أو do...end بناء الجملة.ما يجعلها مختلفة عن لامدا (أو "إغلاق الوظيفة") هو أنها لا تقدم مستوى اتصال جديد. إذا تم استدعاء الكود الموجود في نص إغلاق الكتلة return, ، فهو يعود من الوظيفة/الطريقة الخارجية التي يوجد بها إغلاق الكتلة، وليس فقط الكتلة نفسها.

يعد هذا السلوك أمرًا بالغ الأهمية للحفاظ على ما قام به R.D.أطلق Tennent على "مبدأ المراسلة"، والذي ينص على أنه يجب أن تكون قادرًا على استبدال أي رمز بوظيفة مضمنة تحتوي على هذا الرمز في النص ويتم استدعاؤه على الفور.على سبيل المثال، في Javascript، يمكنك استبدال هذا:

x=2
console.log(x)

مع هذا:

(function(){x = 2;})();
console.log(x)

هذا المثال ليس مثيرًا للاهتمام، لكن القدرة على القيام بهذا النوع من التحويل دون التأثير على سلوك البرنامج تلعب دورًا رئيسيًا في إعادة الهيكلة الوظيفية.ولكن مع لامدا، بمجرد تضمينها return التصريحات، لم يعد المبدأ قائمًا:

function foo1() {
  if (1) {
    return;
  }
  console.log("foo1: This should never run.")
}
foo1()
function foo2() {
  if (1) {
    (function() { return; })();
  }
  console.log("foo2: This should never run.")
}
foo2()

الوظيفة الثانية تختلف عن الأولى؛ال console.log يتم تنفيذه، لأن return يعود فقط من الدالة المجهولة، وليس من foo2.وهذا يكسر مبدأ المراسلات.

هذا هو السبب في أن روبي لديها كل من procs و lambdas، على الرغم من أن التمييز هو مصدر دائم للارتباك للمبتدئين.كل من procs و lambdas كائنات من الطبقة Proc, ، لكنهم يتصرفون بشكل مختلف، كما هو موضح أعلاه:أ return يعود فقط من جسم لامدا، لكنه يعود من الطريقة المحيطة بالبروك.

def test
  p = proc do return 1 end
  l = lambda do return 1 end
  r = l[]
  puts "Called l, got #{r}, still here."
  r = p[]
  puts "Called p, got #{r}, still here?"
end

ما سبق test لن تصل الطريقة أبدًا إلى الثانية puts, ، لأن الدعاء p سوف يسبب test للعودة فورًا (بقيمة إرجاع 1).إذا كانت جافا سكريبت تحتوي على عمليات إغلاق جماعية، فيمكنك فعل الشيء نفسه، لكنها لا تفعل ذلك (على الرغم من وجود اقتراح لإضافتها).

هذا ما يقوله الشخص الملتحي الصاخب عن عمليات الإغلاق والكتل:

http://martinfowler.com/bliki/Closure.html

في مرحلة ما، قال إن الإغلاق عبارة عن كتلة يمكن تمريرها كوسيطة إلى إحدى الطرق.

المصطلحات التي تستخدمها هي الأكثر استخدامًا معاً هذه الأيام في روبي، على الرغم من أن البنيات ظهرت سابقًا في Algol وSmalltalk وScheme.أود أن أقتبس معيار روبي، إذا كان هناك واحد.

لست متأكدًا من قدرتي على الإجابة على سؤالك بالضبط، لكن يمكنني التوضيح.أعتذر إذا كنت تعرف هذا بالفعل ...

def f &x
  yield
  x
end

def g
  y = "block"
  t = f { p "I'm a #{y}" }
  y = "closure"
  t
end

t = g
t.call

و...

$ ruby exam.rb
"I'm a block"
"I'm a closure"
$ 

لذلك هي كتلة تسلسل مجهول يشبه الوظيفة من التعليمات البرمجية المرفقة باستدعاء الأسلوب.يتم استخدامه في جميع أنحاء Ruby API.عندما تجعل من السهل بما فيه الكفاية إنشاء وظيفة مجهولة، يتبين أنها مفيدة لجميع أنواع الأشياء.

لكن لاحظ أنه بعد ذلك f يعود إذن g يعود، لقد تمسكنا بالكتلة بإعادتها من f (مثل x) ثم من g (مثل t).الآن نسمي الكتلة مرة ثانية.مرة أخرى، لاحظ ذلك g() لقد عاد.لكن الكتلة تشير إلى متغير محلي في مثيل دالة (ونطاقها) لم يعد موجودًا؟!ويحصل على القيمة الجديدة y?!

لذلك هو الإغلاق كائن يشبه الوظيفة مغلق على نطاقه المعجمي.إنها صعبة التنفيذ للغاية لأنها تدمر نموذج "افعل ذلك باستخدام المكدس" المفيد جدًا للمتغيرات المحلية في مثيلات استدعاء الوظائف.


1.لدى روبي نكهات مختلفة من الكائنات الوظيفية الشبيهة بالإغلاق؛هذا فقط واحد منهم.

5

هذا هو عدد صحيح.

إنت WorkDaysInAWeek = 5

هذا هو متغير عدد صحيح ويمكن تعيينه إلى مختلف عدد صحيح.(إذا منعت الظروف من تعديل تلك القيمة، فقد يطلق عليها اسم a ثابت.)

وبما أن الأرقام المذكورة أعلاه تتعلق بالأرقام، كتل و الإغلاق خوارزميات القلق.التمييز بين كتل و الإغلاق, على التوالي، وهو ما يعادل أيضا ما ورد أعلاه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top