'แลมด้า' ที่ทุกคนพูดถึงคืออะไร?


93

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

ใครช่วยให้ความกระจ่างฉันเกี่ยวกับคุณค่าที่แท้จริงของมัน


16
ฉันจะชี้ให้ผู้ตอบทราบได้หรือไม่ว่าผู้ถามไม่มีที่ไหนกล่าวถึง. net

1
ตรวจสอบที่เกี่ยวข้อง qn stackoverflow.com/questions/16501/what-is-a-lambda-function
yesraaj

แตกคำถาม ขอบคุณที่ถาม.
Peanut

Lambdas อยู่ในโลกของการเขียนโปรแกรมเชิงฟังก์ชัน (การเขียนโปรแกรมแบบสำแดง)
RBT

คำตอบ:


179

ฟังก์ชันที่ไม่มีชื่อ

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

function () {}; // very simple

เรามาดูการใช้งาน lambdas เหล่านี้กัน

การแยกรหัสสำเร็จรูป

Lambdas อาจใช้เพื่อแยกรหัสสำเร็จรูป ตัวอย่างเช่นลูป เราคุ้นเคยกับการเขียนforและwhileวนซ้ำอยู่ตลอดทั้งวัน แต่นี่เป็นรหัสที่ไม่ได้เขียน เราสามารถดึงรหัสภายในลูปซึ่งเป็นส่วนที่สำคัญที่สุดของลูปและแยกส่วนที่เหลือออกไป:

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

โดยใช้forEachวัตถุอาร์เรย์กลายเป็น:

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

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

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

ความล่าช้าในการดำเนินการรหัส

ในบางสภาพแวดล้อมที่มีแนวคิดของเหตุการณ์เราสามารถใช้ lambdas เพื่อตอบสนองต่อเหตุการณ์ที่อาจเกิดขึ้นในบางช่วงเวลา

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

สิ่งนี้สามารถทำได้ด้วยวิธีอื่น ๆ แต่ก็ค่อนข้างละเอียด ตัวอย่างเช่นใน Java มีRunnableอินเทอร์เฟซ

โรงงานของฟังก์ชั่น

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

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

ในภายหลังเราอาจสังเกตเห็นว่าเรามีหน้าที่คล้ายกัน:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

มีรูปแบบอยู่ในนั้นอย่างชัดเจนดังนั้นเรามาสรุปกันดีกว่า มาใช้การบันทึกกันเถอะ

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

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

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

เป็นตัวอย่างของการใช้ Python lambda โค้ดด้านบนที่เรากรองเลขคู่สามารถแสดงใน Python ได้ดังนี้:

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

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


3
ว้าวคุณทุ่มเทเวลาให้มาก
mk12

4
@ Mk12 ในการเขียนคำตอบจริงไม่จริง ในการเรียนรู้สิ่งนี้ใช่มันเป็นเวลานานแล้วที่ฉันเริ่ม :)
Ionuț G. Stan

คำตอบที่ดี แต่ไม่มีข้อมูลเกี่ยวกับ "อินเตอร์เฟสการทำงาน" (จากมุมมอง Java)
djangofan

โอเปอเรเตอร์ "===" เหล่านั้นใน "รหัสสำเร็จรูปที่เป็นนามธรรม" คืออะไร
ดอน


19

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

(1..100).select {|num| num % 2 == 0}

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

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


6
บางทีคุณควรเพิ่มว่าสิ่งที่อยู่ในท่อคือพารามิเตอร์ ฉันเป็นหนึ่งในคนเหล่านี้ที่มีปัญหาในการอ่านทับทิม
Skurmedel

นี่คือคำตอบที่ดี เหตุผลเดียวที่ Ionut ได้รับการโหวตของฉันคือเขาบอกเราว่าทำไมเราควรใส่ใจ (โดยละเอียด) เกี่ยวกับ lambdas
Frank Shearar

ฉันไม่เห็นด้วยที่ lambdas มักจะปิด
jwg

6

"แลมด้า" อาจจะน้อยเกินไป ลองดูที่แลมบ์ดาแคลคูลัส มีประโยชน์ในการเขียนโปรแกรมเชิงฟังก์ชัน

และการเขียนโปรแกรมเชิงฟังก์ชันก็เป็นอีกกระบวนทัศน์การเขียนโปรแกรม (เช่นขั้นตอนหรือเชิงวัตถุ)


5
จากนั้นผู้คนมักพูดถึง "แลมด้า" ซึ่งส่วนใหญ่แล้วพวกเขากำลังพูดถึงฟังก์ชันที่ไม่ระบุตัวตนตัวชี้ฟังก์ชันการปิดหรือสิ่งที่คล้ายกัน แทบไม่เคยอ้างถึงสิ่งแคลคูลัสแลมบ์ดาที่แท้จริง
J-16 SDiZ

ดีใจที่คุณพูดถึงแคลคูลัสแลมบ์ดา! +1.
RBT

5

Lambdas ใน. NET มักเรียกกันว่า "syntactic sugar" ไม่ส่งผลกระทบโดยตรงต่อฟังก์ชันการทำงาน แต่ทำให้ภาษาสำหรับผู้ใช้ง่ายขึ้น

เมื่อคุณเข้าใจถึงพลังของการใช้งานแล้วฉันมั่นใจว่าคุณจะพบว่าคุณจะเขียนโค้ดน้อยลงเมื่อเทียบกับวิธีการแบบเก่าโดยใช้วิธีการมอบหมาย / ไม่ระบุตัวตน


1
ฉันไม่คิดว่าจะมีใครพูดถึง. NET ดังนั้น OP จึงน่าจะดีกว่าด้วยคำตอบทั่วไป
molf

2
นั่นคือเหตุผลที่ฉันชี้แจงด้วยคำตอบเกี่ยวกับ. net หากคนอื่น ๆ พูดถึงการใช้ภาษาของพวกเขาคำถาม & ตอบจะช่วยให้หลาย ๆ คนไม่ว่าพวกเขาจะเลือกภาษา
redsquare

คำตอบนี้อ่านเหมือนคุณเคยได้ยินบางอย่างเกี่ยวกับ lambdas แต่คุณเองยังไม่เข้าใจ
jwg

2

Dr Dobbs Journalมีบทความที่เป็นประโยชน์เกี่ยวกับนิพจน์แลมบ์ดา (ภายในบริบทของ C ++ แต่ฉันคิดว่าคุณสามารถใช้หลักการนี้กับภาษาใดก็ได้)

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

ดังนั้นใช้ตัวอย่างรายชื่อ 1 & 2 จากDDJแทนการเขียน:

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

ซึ่งต้องมีการกำหนดคลาสแยกต่างหากเช่น:

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

การใช้ไลบรารี Boost lambdaสิ่งนี้สามารถกลายเป็น:

std::for_each(vec.begin(),vec.end(),std::cout << _1);

ซึ่งจะคงความหมายไว้ในบรรทัด

บทความนี้ยังอธิบายถึงการประยุกต์ใช้นิพจน์แลมบ์ดาเพิ่มเติม

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


2

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


2

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

นักคณิตศาสตร์และนักตรรกะจะได้รับการใช้คำแปลก ๆ

'lambda' ฟังดูลึกลับมากราวกับว่ามันเป็นสิ่งที่แปลกและพิเศษมาก จริงๆแล้วถ้าคุณเขียน JavaScript สำหรับแอปพลิเคชันเว็บเบราว์เซอร์และใช้สำนวน "var foo = function () {... }" แสดงว่าคุณใช้ฟังก์ชันแลมบ์ดามาตลอด


1

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

ตัวอย่างเช่นใน c sharp:

x => x * x

คือแลมด้ากำลังสองค่า บางอย่างของแบบฟอร์ม

x

กลายเป็นรูปแบบ

x * x

0

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

นิพจน์แลมบ์ดาทั้งหมดใช้ตัวดำเนินการแลมบ์ดา => ซึ่งอ่านว่า "ไปที่" ด้านซ้ายของตัวดำเนินการแลมบ์ดาระบุพารามิเตอร์อินพุต (ถ้ามี) และด้านขวาเก็บนิพจน์หรือบล็อกคำสั่ง นิพจน์แลมบ์ดา x => x * x อ่านว่า "x ไปที่ x คูณ x"

จากMSDN


4
ใช่ Microsoft ทำให้ทุกคนคิดว่าพวกเขาเป็นผู้คิดค้นมันขึ้นมา นิพจน์แลมบ์ดามาก่อน Microsoft แม้ว่า เป็นคำศัพท์ทางคณิตศาสตร์ที่ใช้กับภาษาโปรแกรมต่างๆ (ซึ่งเป็นไปได้เนื่องจากคณิตศาสตร์ถือได้ว่าเป็นภาษาคอมพิวเตอร์ด้วยตัวเอง)
Wim ten Brink

4
โปรดทราบว่านี่เป็นสิ่งเฉพาะสำหรับการใช้งาน. Net ของ Microsoft มันไม่ได้เบี่ยงเบนไปจากแนวคิดทั่วไปของแลมด้ามากนัก แต่ฉันคิดว่าฟังก์ชันที่นำมาใช้ใน Lisp นั้นเป็น "มาตรฐาน" มากกว่า
Chuck

2
คำตอบนั้นไม่ได้อธิบายเลยว่า Lambda คืออะไร (ต้องการคำจำกัดความของฟังก์ชันที่ไม่ระบุตัวตนผู้รับมอบสิทธิ์ประเภทแผนภูมินิพจน์) และไม่ได้อธิบายว่าค่าของมันคืออะไร
danio

0

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

คุ้มมั้ย? เมื่อพิจารณาว่ามันค่อนข้างเก่าแล้วฉันจะบอกว่ามีค่ามากสำหรับทุกคนที่ทำการคำนวณ


0

หากคุณใช้ Java คุณเคยได้ยินเกี่ยวกับ lambdas หรือการปิดตัวลงมากในช่วงสองสามเดือนที่ผ่านมาเนื่องจากมีข้อเสนอที่แตกต่างกันสำหรับการเพิ่มคุณสมบัตินี้ใน Java 7 อย่างไรก็ตามฉันคิดว่า comitee ทิ้งมันไป หนึ่งในข้อเสนอที่มาจากโอนีล Gafter และอธิบายในรายละเอียดมากที่นี่: javac.info สิ่งนี้ช่วยให้ฉันเข้าใจกรณีการใช้งานและข้อดี (โดยเฉพาะในชั้นเรียนภายใน)




-1

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

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

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

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