سؤال

أنا الآن فقط التعلم عن مؤشرات دالة ، كما كنت أقرأ K&R الفصل في هذا الموضوع الشيء الأول الذي ضرب لي "مرحبا, هذا هو كيندا مثل الإغلاق." كنت أعرف أن هذا الافتراض خاطئ في الأساس بطريقة أو بأخرى و بعد البحث على الانترنت لم أجد أي تحليل هذه المقارنة.

فلماذا ج-أسلوب مؤشرات الدالة مختلفة جذريا من الإغلاق أو lambdas?بقدر ما أستطيع أن أقول ذلك له علاقة مع حقيقة أن وظيفة المؤشر لا يزال يشير إلى تعريف (اسمه) وظيفة بدلا من ممارسة مجهول تعريف الدالة.

لماذا يمر وظيفة إلى وظيفة تعتبر أكثر قوة في الحالة الثانية ، حيث أنه لم يكشف عن اسمه, من حيث هو مجرد اليومية العادية وظيفة التي يتم مرت ؟

من فضلك قل لي كيف و لماذا أنا من الخطأ المقارنة بينهما بشكل وثيق.

شكرا

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

المحلول

وA امدا (أو إغلاق ) بتغليف كل من مؤشر الدالة والمتغيرات. هذا هو السبب، في C #، يمكنك القيام به:

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

وكنت مندوب مجهول هناك، وإغلاق (جملة ذلك هو قليلا أكثر وضوحا وأقرب إلى C من ما يعادل امدا) التي احتلتها lessThan (متغير كومة) في الإغلاق. عندما يتم تقييم الإغلاق، وlessThan (الذي قد دمرت إطار مكدس) يستمر المشار إليه. إذا قمت بتغيير lessThan، ثم أقوم بتغيير المقارنة:

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

lessThanTest(99); // returns true
lessThan = 10;
lessThanTest(99); // returns false

في C، وهذا سيكون غير قانوني:

BOOL (*lessThanTest)(int);
int lessThan = 100;

lessThanTest = &LessThan;

BOOL LessThan(int i) {
   return i < lessThan; // compile error - lessThan is not in scope
}

وعلى الرغم من أنني يمكن تحديد مؤشر دالة التي تأخذ 2 الحجج:

int lessThan = 100;
BOOL (*lessThanTest)(int, int);

lessThanTest = &LessThan;
lessThanTest(99, lessThan); // returns true
lessThan = 10;
lessThanTest(100, lessThan); // returns false

BOOL LessThan(int i, int lessThan) {
   return i < lessThan;
}

ولكن، الآن لدي لتمرير الحجج 2 عندما تقييمها. إذا كنت ترغب في تمرير هذا المؤشر وظيفة إلى وظيفة أخرى حيث كان lessThan ليس في نطاق، وأود إما أن تبقي يدويا على قيد الحياة بتمريرها لكل وظيفة في السلسلة، أو الترويج لها على المستوى العالمي.

وعلى الرغم من أن معظم اللغات الرئيسية التي تدعم الإغلاق استخدام وظائف غير معروفة وليس هناك شرط لذلك. هل يمكن أن يكون الإغلاق دون وظائف مجهولة، وظائف مجهولة دون الإغلاق.

موجز: إغلاق هو مزيج من وظيفة مؤشر + المتغيرات القبض

نصائح أخرى

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

الإغلاق هو مجرد تجميع يحتوي على وظيفة و البيئة.في مستوى مل من نيو جيرسي مترجم, نحن ممثلة واحدة كما سجل ؛ واحد الميدانية الواردة مؤشر إلى الرمز ، وغيرها من المجالات الواردة قيم المتغيرات الحرة.مترجم خلق جديد الإغلاق (لا تعمل) بشكل حيوي من خلال تخصيص رقم قياسي جديد يحتوي على مؤشر نفس مدونة, ولكن مع مختلفة قيم المتغيرات الحرة.

يمكنك محاكاة كل هذا في C ، ولكن الألم في المؤخرة.اثنين من التقنيات شعبية:

  1. تمرير مؤشر إلى وظيفة (رمز) منفصلة مؤشر إلى المتغيرات الحرة ، حيث أن الإغلاق هو الانقسام عبر اثنين ج المتغيرات.

  2. تمرير مؤشر إلى البنية ، حيث البنية يحتوي على قيم المتغيرات الحرة و أيضا مؤشر إلى الرمز.

الأسلوب #1 مثالية عندما كنت في محاولة لمحاكاة بعض نوع من تعدد الأشكال في C و أنت لا تريد أن تكشف عن نوع من البيئة---يمكنك استخدام الفراغ* مؤشر لتمثيل البيئة.للحصول على أمثلة أنظر ديف هانسون ج واجهات تطبيقات.الأسلوب #2, التي تشبه ما يحدث في الأم رمز المجمعين الوظيفي اللغات أيضا يشبه آخر تقنية مألوفة...C++ مع كائنات افتراضية وظائف الأعضاء.التطبيقات هي متطابقة تقريبا.

هذه الملاحظة أدت إلى ويسيكراك من هنري بيكر:

الناس في Algol/Fortran العالم اشتكى لسنوات أنهم لم يفهموا ما ممكن استخدام وظيفة الإغلاق في البرمجة الفعالة في المستقبل.ثم `وجوه المنحى البرمجة' الثورة حدث و الآن الجميع البرامج باستخدام وظيفة الإغلاق ، إلا أنها لا تزال ترفض أن ندعو لهم ذلك.

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

في أبسط الشروط، مؤشرات الدالة ليس لها نطاق المرتبطة بها (إلا إذا كنت تعول على النطاق العالمي)، في حين تشمل إغلاق نطاق الأسلوب الذي تم تحديدها. مع lambdas، يمكنك كتابة الأسلوب الذي يكتب الأسلوب. إغلاق تسمح لك لربط "بعض الحجج إلى وظيفة والحصول على وظيفة المنخفض arity نتيجة لذلك." (مأخوذة من تعليق توماس). لا يمكنك أن تفعل ذلك في C.

وتحرير: إضافة مثال (انا ذاهب الى استخدام القضية جملة أكشن العش وهذا ما يدور في ذهني الآن):

ويقول لديك بعض الطرق التي تأخذ طريقة أخرى كما حجتها، ولكنها لا توفر وسيلة لتمرير أية معلمات لهذه الطريقة عندما يسمى ذلك؟ مثل، مثلا، بعض الطرق التي تسبب تأخير قبل تشغيل الأسلوب الذي مرت عليه (على سبيل المثال غبي، ولكن أريد أن يبقيه بسيط).

function runLater(f:Function):Void {
  sleep(100);
  f();
}

والآن نقول لكم تريد runLater المستخدم () لتأخير بعض تجهيز كائن:

function objectProcessor(o:Object):Void {
  /* Do something cool with the object! */
}

function process(o:Object):Void {
  runLater(function() { objectProcessor(o); });
}

وظيفة كنت تمر لعملية () ليست دالة معرفة بعض staticly بعد الآن. انها ديناميكيا، وغير قادرة على إدراج إشارات إلى المتغيرات التي كانت في نطاق عندما تم تعريف الأسلوب. لذلك، فإنه يمكن الوصول إلى 'س' و 'objectProcessor "، على الرغم من أن تلك ليست في نطاق عالمي.

وآمل أن من المنطقي.

وإغلاق = منطق + البيئة.

وعلى سبيل المثال، والنظر في هذه الطريقة C # 3:

public Person FindPerson(IEnumerable<Person> people, string name)
{
    return people.Where(person => person.Name == name);
}

والتعبير امدا ليس فقط بتغليف منطق ( "مقارنة اسم")، ولكن أيضا على البيئة، بما في ذلك المعلمة (أي متغير محلي) "اسم".

لمزيد من المعلومات حول هذا، إلقاء نظرة على بلدي على إغلاق الذي يأخذك من خلال C # 1 و 2 و 3، والتي تبين كيف تجعل الأمور أسهل الإغلاق.

في C ، مؤشرات الدالة يمكن أن تنتقل كما الحجج وظائف إرجاع القيم من الوظائف ، ولكن الوظائف موجودة فقط في المستوى الأعلى:لا يمكنك عش تعريفات الدالة داخل بعضها البعض.التفكير في ما سيستغرق ج لدعم متداخلة المهام التي يمكن الوصول إلى المتغيرات الخارجية وظيفة ، في حين لا تزال قادرة على إرسال مؤشرات الدالة صعودا وهبوطا مكدس الاستدعاءات.(لمتابعة هذا التفسير يجب أن تعلم أساسيات كيفية المكالمات وظيفة تنفذ في ج و على غرار معظم اللغات:من خلال تصفح مكدس الاستدعاءات الدخول على ويكيبيديا.)

أي نوع من الكائن هو مؤشر إلى متداخلة وظيفة ؟ لا يمكن أن تكون فقط عنوان المدونة, لأنه إذا كنت أسميها كيف الوصول إلى المتغيرات الخارجية وظيفة ؟ (تذكر أنه بسبب العودية, قد يكون هناك عدة مكالمات مختلفة الخارجية وظيفة نشطة في وقت واحد.) وهذا ما يسمى funarg المشكلة, و هناك نوعان من المسائل الفرعية للحصول:الانخفاض funargs المشكلة صعودا funargs المشكلة.

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

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

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

وA امدا هو وظيفة مجهول، <م> تعريف بشكل حيوي. انك لا تستطيع أن تفعل ذلك في C ... كما لإغلاق (أو convination من اثنين)، والمثال النموذجي ثغة سيبدو شيئا على غرار:

(defun get-counter (n-start +-number)
     "Returns a function that returns a number incremented
      by +-number every time it is called"
    (lambda () (setf n-start (+ +-number n-start))))

في المصطلحات C، يمكن القول أن البيئة المعجمية (كومة) من get-counter يتم التقاطها بواسطة وظيفة غير معروفة ووتعديلها داخليا كما يوضح المثال التالي:

[1]> (defun get-counter (n-start +-number)
         "Returns a function that returns a number incremented
          by +-number every time it is called"
        (lambda () (setf n-start (+ +-number n-start))))
GET-COUNTER
[2]> (defvar x (get-counter 2 3))
X
[3]> (funcall x)
5
[4]> (funcall x)
8
[5]> (funcall x)
11
[6]> (funcall x)
14
[7]> (funcall x)
17
[8]> (funcall x)
20
[9]> 

والإغلاق يعني لا بد بعض متغير من وجهة تعريف الدالة جنبا إلى جنب مع منطق وظيفة، مثل القدرة على إعلان وجوه صغيرة على الطاير.

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

وأنا لست مرتاحا مساواة امدا مع إغلاق لأنني لست متأكدا من أن lambdas بجميع اللغات والإغلاق، في بعض الأحيان أعتقد lambdas للتو تم تعريف محليا وظائف مجهولة دون ربط المتغيرات (بيثون ما قبل 2.1؟).

في دول مجلس التعاون الخليجي من الممكن لمحاكاة وظائف لامدا باستخدام الماكرو التالي:

#define lambda(l_ret_type, l_arguments, l_body)       \
({                                                    \
    l_ret_type l_anonymous_functions_name l_arguments \
    l_body                                            \
    &l_anonymous_functions_name;                      \
})

مثال من مصدر :

qsort (array, sizeof (array) / sizeof (array[0]), sizeof (array[0]),
     lambda (int, (const void *a, const void *b),
             {
               dump ();
               printf ("Comparison %d: %d and %d\n",
                       ++ comparison, *(const int *) a, *(const int *) b);
               return *(const int *) a - *(const int *) b;
             }));

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

و <م> إغلاق يلتقط <م> متغيرات المجانية في البيئة . فإن البيئة لا تزال موجودة، على الرغم من أن قانون المحيطة قد لا تكون نشطة.

ومثال على ذلك في اللثغة المشتركة، حيث يعود MAKE-ADDER إغلاق الجديد.

CL-USER 53 > (defun make-adder (start delta) (lambda () (incf start delta)))
MAKE-ADDER

CL-USER 54 > (compile *)
MAKE-ADDER
NIL
NIL
<ع> استخدام الدالة أعلاه:

CL-USER 55 > (let ((adder1 (make-adder 0 10))
                   (adder2 (make-adder 17 20)))
               (print (funcall adder1))
               (print (funcall adder1))
               (print (funcall adder1))
               (print (funcall adder1))
               (print (funcall adder2))
               (print (funcall adder2))
               (print (funcall adder2))
               (print (funcall adder1))
               (print (funcall adder1))
               (describe adder1)
               (describe adder2)
               (values))

10 
20 
30 
40 
37 
57 
77 
50 
60 
#<Closure 1 subfunction of MAKE-ADDER 4060001ED4> is a CLOSURE
Function         #<Function 1 subfunction of MAKE-ADDER 4060001CAC>
Environment      #(60 10)
#<Closure 1 subfunction of MAKE-ADDER 4060001EFC> is a CLOSURE
Function         #<Function 1 subfunction of MAKE-ADDER 4060001CAC>
Environment      #(77 20)

لاحظ أن الدالة DESCRIBE يدل على أن <م> وظيفة الأجسام لكل من <م> إغلاق هي نفسها، ولكن البيئة هو مختلف.

واللثغة المشتركة يجعل كلا الإغلاق والأجسام وظيفة نقية (من دون بيئة) على حد سواء أن تكون <م> وظائف واحد يمكن أن نطلق على حد سواء في نفس الطريق، وهنا باستخدام FUNCALL.

والفرق الرئيسي ينشأ من عدم وجود تحديد النطاق المعجمي في C.

ومؤشر وظيفة هو أن مؤشر كتلة من مجرد رمز. أي متغير غير مكدس أنه يحيل عالمي، ثابت أو ما شابه ذلك.

والإغلاق، OTOH، له دولة خاصة به في شكل 'المتغيرات الخارجية'، أو 'upvalues. أنها يمكن أن تكون خاصة أو مشتركة على النحو الذي تريد، وذلك باستخدام الفحص المعجمي. يمكنك إنشاء الكثير من الإغلاق مع نفس رمز وظيفة، ولكن المتغيرات المختلفة الحالات.

وهناك عدد قليل من إغلاق يمكن مشاركة بعض المتغيرات، وهكذا يمكن أن تكون واجهة للكائن (بالمعنى OOP). لجعل هذا في C لديك لربط هيكل مع جدول مؤشرات الدالة (وهذا ما يفعله C ++، مع vtable الدرجة).

وباختصار، إغلاق هو مؤشر دالة PLUS بعض الدول. انها بناء على مستوى أعلى

ومعظم الردود تشير إلى أن إغلاق تتطلب مؤشرات الدالة، وربما إلى وظائف غير معروفة ولكن كما <لأ href = "https://stackoverflow.com/questions/208835/function-pointers-closures-and-lamda#208940" > كتب مارك يمكن إغلاق موجودة مع وظائف الكشف عن اسمه. وإليك مثالا في بيرل:

{
    my $count;
    sub increment { return $count++ }
}

وإغلاق هي البيئة التي تعرف المتغير $count. وهي متاحة فقط لروتين increment واستمرت بين المكالمات.

في C مؤشر دالة هو المؤشر الذي سوف تحتج وظيفة عند dereference ذلك، والإغلاق هو القيمة التي تحتوي على المنطق وظيفة والبيئة (المتغيرات والقيم التي لا بد أن)، وامدا عادة ما يشير إلى قيمة هذا هو في الواقع وظيفة غير مسمى. في C وظيفة ليست قيمة الدرجة الأولى لذلك لا يمكن أن تنتقل حول بحيث يكون لديك لتمرير مؤشر إلى أنه بدلا من ذلك، ولكن في اللغات الوظيفية (مثل مخطط) يمكنك تمرير وظائف في بنفس الطريقة التي تمرر أي قيمة أخرى

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