'แลมด้า' ที่ทุกคนพูดถึงคืออะไร? หลายคนดูเหมือนจะชอบมัน แต่ทั้งหมดที่ฉันรวบรวมได้จากมันก็เป็นเพียงวิธีการยัดโค้ดหลายบรรทัดลงในนิพจน์เดียว
ใครช่วยให้ความกระจ่างฉันเกี่ยวกับคุณค่าที่แท้จริงของมัน
'แลมด้า' ที่ทุกคนพูดถึงคืออะไร? หลายคนดูเหมือนจะชอบมัน แต่ทั้งหมดที่ฉันรวบรวมได้จากมันก็เป็นเพียงวิธีการยัดโค้ดหลายบรรทัดลงในนิพจน์เดียว
ใครช่วยให้ความกระจ่างฉันเกี่ยวกับคุณค่าที่แท้จริงของมัน
คำตอบ:
พูดง่ายๆคือแลมบ์ดาเป็นฟังก์ชันที่ไม่มีชื่อหรือฟังก์ชันที่ไม่ระบุตัวตน โค้ดปฏิบัติการชิ้นเล็ก ๆ ที่สามารถส่งผ่านไปได้ราวกับว่ามันเป็นตัวแปร ใน 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
ฟังก์ชันจะส่งคืนผลลัพธ์แล้วก็ตาม (แลมบ์ดา)
คำว่า "แลมบ์ดา" ถูกนำมาใช้ในการอ้างถึงฟังก์ชั่นที่ไม่ระบุชื่อมักจะปิด มีประโยชน์เพราะช่วยให้คุณสามารถเขียนฟังก์ชันที่ใช้ฟังก์ชันอื่น ๆ ได้โดยไม่ทำให้โค้ดของคุณพองโดยไม่จำเป็น ตัวอย่างเช่นใน Ruby:
(1..100).select {|num| num % 2 == 0}
สิ่งนี้จะสร้างอาร์เรย์ที่มีเลขคู่ระหว่าง 1 ถึง 100 เราไม่จำเป็นต้องเขียนลูปที่ชัดเจน - วิธีการเลือกจะใช้ฟังก์ชันที่ใช้ทดสอบค่าดังนั้นสิ่งที่เราต้องการคือตรรกะที่กำหนดเอง สิ่งนี้ช่วยให้เราสามารถปรับแต่งวิธีการได้อย่างมากโดยไม่ต้องใช้ความพยายามหรือค่าใช้จ่ายใด ๆ โดยพื้นฐานแล้วเราสามารถเขียนฟังก์ชันจากฟังก์ชันที่เล็กกว่าได้
นั่นเป็นเพียงตัวอย่างง่ายๆของสิ่งที่พวกเขาทำได้ ความสามารถในการส่งผ่านฟังก์ชั่นเนื่องจากข้อมูลเป็นโปรแกรมเมอร์ภาษาที่มีประสิทธิภาพและใช้งานได้เป็นประจำมักจะทำสิ่งที่น่าทึ่งกับมัน
Lambdas ใน. NET มักเรียกกันว่า "syntactic sugar" ไม่ส่งผลกระทบโดยตรงต่อฟังก์ชันการทำงาน แต่ทำให้ภาษาสำหรับผู้ใช้ง่ายขึ้น
เมื่อคุณเข้าใจถึงพลังของการใช้งานแล้วฉันมั่นใจว่าคุณจะพบว่าคุณจะเขียนโค้ดน้อยลงเมื่อเทียบกับวิธีการแบบเก่าโดยใช้วิธีการมอบหมาย / ไม่ระบุตัวตน
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คือ "โดยทั่วไปแล้วนิพจน์แลมบ์ดาจะใช้เมื่อต้องการฟังก์ชันขนาดเล็กและไม่ซับซ้อนเกินไปที่ไซต์การโทรหากฟังก์ชันไม่สำคัญคุณคงไม่ต้องการนิพจน์แลมบ์ดา แต่เป็นฟังก์ชันปกติหรือ วัตถุฟังก์ชัน "
หากคุณเคยทำงานกับฟังก์ชัน / วิธีการที่ใช้ตัวชี้ฟังก์ชันผู้รับมอบสิทธิ์กลยุทธ์หรือรูปแบบผู้สังเกตการณ์ / การจัดการเหตุการณ์และคิดกับตัวเองว่า "ฉันกำลังเขียนฟังก์ชันทั้งหมดนี้เพื่อใช้เพียงครั้งเดียว - เพื่อส่งต่อไปยังวิธีนี้ ; ฉันหวังว่าฉันจะเขียนมันแทนแทนที่จะทำให้โค้ดของฉันยุ่งเหยิง "- นั่นคือที่ที่คุณอาจใช้ฟังก์ชัน Lambda ภาษาที่รองรับโครงสร้างนี้มักจะใช้ประโยชน์จากแนวคิดของการส่งผ่านฟังก์ชันเป็นพารามิเตอร์โดยเฉพาะอย่างยิ่งในการทำงานกับรายการ (ฟังก์ชันชั้นหนึ่งและฟังก์ชันลำดับที่สูงกว่า) โดยเฉพาะอย่างยิ่งสำหรับภาษาที่ใช้งานได้ซึ่งอาศัยองค์ประกอบของฟังก์ชันมากกว่าการปรับเปลี่ยนหน่วยความจำสำหรับการคำนวณ ในบางกรณี (ในภาษาเช่น Python)
คำว่า 'lambda' เป็นคำศัพท์จากสมัยที่วิทยาศาสตร์คอมพิวเตอร์มีแนวโน้มที่จะได้รับการฝึกฝนด้านคณิตศาสตร์หรือตรรกะมากกว่าที่จะจบปริญญาวิทยาศาสตร์คอมพิวเตอร์ พวกเขาบางคนปรุงกระบวนทัศน์ที่เรียกว่า 'การเขียนโปรแกรมเชิงฟังก์ชัน' ซึ่งค่อนข้างแตกต่างจากความจำเป็นและมีพลังมากเกินไป AFAIKนั่นคือสภาพแวดล้อมที่มีการใช้คำนี้
นักคณิตศาสตร์และนักตรรกะจะได้รับการใช้คำแปลก ๆ
'lambda' ฟังดูลึกลับมากราวกับว่ามันเป็นสิ่งที่แปลกและพิเศษมาก จริงๆแล้วถ้าคุณเขียน JavaScript สำหรับแอปพลิเคชันเว็บเบราว์เซอร์และใช้สำนวน "var foo = function () {... }" แสดงว่าคุณใช้ฟังก์ชันแลมบ์ดามาตลอด
นิพจน์แลมบ์ดาเป็นฟังก์ชันรูปแบบง่ายๆ แนวคิดก็คือบางสิ่งในรูปแบบทางด้านซ้าย (เทียบเท่ากับพารามิเตอร์) จะกลายเป็นรูปแบบทางด้านขวา (เทียบเท่ากับร่างกาย)
ตัวอย่างเช่นใน c sharp:
x => x * x
คือแลมด้ากำลังสองค่า บางอย่างของแบบฟอร์ม
x
กลายเป็นรูปแบบ
x * x
"นิพจน์แลมบ์ดาเป็นฟังก์ชันที่ไม่ระบุตัวตนซึ่งสามารถมีนิพจน์และข้อความสั่งและสามารถใช้เพื่อสร้างผู้รับมอบสิทธิ์หรือประเภทแผนภูมิของนิพจน์
นิพจน์แลมบ์ดาทั้งหมดใช้ตัวดำเนินการแลมบ์ดา => ซึ่งอ่านว่า "ไปที่" ด้านซ้ายของตัวดำเนินการแลมบ์ดาระบุพารามิเตอร์อินพุต (ถ้ามี) และด้านขวาเก็บนิพจน์หรือบล็อกคำสั่ง นิพจน์แลมบ์ดา x => x * x อ่านว่า "x ไปที่ x คูณ x"
จากMSDN
สำหรับคำอธิบายทั้งหมดเกี่ยวกับการแสดงออกแลมบ์ดายังตรวจสอบวิกิพีเดีย (เลื่อนลงไปที่ส่วนแคลคูลัสแลมบ์ดาและภาษาการเขียนโปรแกรม ) นิพจน์แลมบ์ดาไม่ใช่เรื่องใหม่และไม่ได้เป็นเพียงส่วนหนึ่งของ C # แต่เป็นสิ่งที่ได้รับการแนะนำให้รู้จักกับการคำนวณเมื่อเกือบ 80 ปีก่อน! นิพจน์แลมบ์ดาเป็นพื้นฐานสำหรับการเขียนโปรแกรมเชิงฟังก์ชัน
คุ้มมั้ย? เมื่อพิจารณาว่ามันค่อนข้างเก่าแล้วฉันจะบอกว่ามีค่ามากสำหรับทุกคนที่ทำการคำนวณ
หากคุณใช้ Java คุณเคยได้ยินเกี่ยวกับ lambdas หรือการปิดตัวลงมากในช่วงสองสามเดือนที่ผ่านมาเนื่องจากมีข้อเสนอที่แตกต่างกันสำหรับการเพิ่มคุณสมบัตินี้ใน Java 7 อย่างไรก็ตามฉันคิดว่า comitee ทิ้งมันไป หนึ่งในข้อเสนอที่มาจากโอนีล Gafter และอธิบายในรายละเอียดมากที่นี่: javac.info สิ่งนี้ช่วยให้ฉันเข้าใจกรณีการใช้งานและข้อดี (โดยเฉพาะในชั้นเรียนภายใน)
คุณจะพบสิ่งที่คุณจำเป็นต้องรู้ (เกี่ยวกับ C # Lambdas) สำหรับการเริ่มต้นที่นี่:
นิพจน์แลมบ์ดา
สำหรับ java lambdas นี่อาจเป็นจุดเริ่มต้นที่ดี:
http://rodrigouchoa.wordpress.com/2014/09/10/java-8-lambda-expressions-tutorial/
ใช่มันเป็นเพียงวิธีการยัดโค้ดหลายบรรทัดลงในนิพจน์เดียว แต่การยัดเยียดที่มีประสิทธิภาพช่วยให้สามารถจัดโครงสร้างโปรแกรมได้ด้วยวิธีใหม่
บ่อยครั้งที่เราจะหลีกเลี่ยงการเขียนผู้รับมอบสิทธิ์หรือการเรียกกลับและเปลี่ยนกลับไปใช้รูปแบบขั้นตอนเพียงเพราะการประกาศฟังก์ชันหรือคลาสใหม่สำหรับนิพจน์เดียวทำงานมากเกินไป
นิพจน์ Lambda ช่วยให้สามารถใช้การเรียกกลับได้อย่างคุ้มค่าแม้ในงานที่เล็กที่สุดซึ่งอาจทำให้โค้ดชัดเจนยิ่งขึ้น อาจจะไม่.