lambda expression – ประโยคสร้างวัตถุแทนฟังก์ชั่น

หลาย ๆ คนน่าจะเคยเห็นฟังก์ชั่นที่รับพารามิเตอร์เป็นฟังก์ชั่นพอยน์เตอร์ในภาษา C หรือเป็นวัตถุที่มีเมธอดเดียว ใช้สำหรับระบุรายละเอียดวิธีการทำงานของฟังก์ชั่นนั้น ๆ (เรียกว่า functor) อย่างในภาษา C++ จะมีฟังก์ชั่นอย่าง std::sort ซึ่งรับพารามิเตอร์ตัวหนึ่งเป็น functor ที่เปรียบเทียบวัตถุสองตัวแล้วบอกว่าตัวไหนน้อยกว่าตัวไหน

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

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

ปัญหาก็คือ … การสร้างเจ้าตัว functor มันวุ่นวายครับ มันจะหน้าตาประมาณนี้ …

struct 
{
    bool operator()(image* a, image* b)
    {   
        return a->area() < b->area();
    }   
} compareArea;

std::vector<image*> vecImage;
std::sort(vecImage.begin(), vecImage.end(), compareArea);

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

lambda expression เป็นฟีเจอร์ใหม่(?) ของหลาย ๆ ภาษาที่อนุญาตให้ผู้ใช้สามารถสร้าง functor จาก expression ได้เลยโดยไม่ต้องประกาศเป็นตัวแปรหรือวัตถุใหม่ จากข้างบนในภาษา C++ เราจะสามารถเขียนด้วย lambda exp ได้แบบนี้ครับ

std::vector<image*> vecImage;
std::sort(vecImage.begin(), vecImage.end(), [](image* a, image* b) {
    return a->area() < b->area();
});

เทียบกับการสร้างวัตถุ + โอเวอร์โหลด operator() แล้ว น่าจะดูง่ายกว่านะครับ แถมไม่ต้องประกาศตัวแปรไปรับก็ได้ เจ๋งใหม่

แต่เราจะประกาศตัวแปรเก็บไว้ใช้ทีหลังก็ได้นะครับ แบบนี้

auto helloWorld = [](){ std::cout<<"Hello World"<<std::endl; };
helloWorld();

การใช้ lambda expression นี่ทำให้เราทำงานกับฟังก์ชั่นใน ได้ง่ายขึ้นเยอะเลยครับ เพราะว่าเฮดเดอร์ชุดนี้มีฟังก์ชั่นที่รับ functor เป็นพารามิเตอร์มากทีเดียว

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

std::vector<image*> vecImage;
std::for_each(vecImage.begin(), vecImage.end(), [this](image* a){
    this->render(a);
});

ผมใช้ [this] แทนที่จะเป็น [] อย่างตัวอย่างข้างบน เพื่อเป็นการระบุว่าในแต่ละ functor ที่เกิดขึ้นนั้นจะเข้าถึง this reference ได้ด้วย

หรืออีกตัวอย่างหนึ่ง

std::vector<int> vecNumber;
int total = 0;
std::for_each(vecNumber.begin(), vecNumber.end(), [&](int a){
    total += a;
});

ถ้าเป็น functor แบบที่ใช้ struct คงเข้าถีึงตัวแปร total ไม่ได้ แต่นี่ไม่ใช่ปัญหาสำหรับ closure ที่เกิดจาก lambda expression ครับ

ตัวอย่างที่ยกมาในครั้งนี้เขียนบนภาษา C++11 ครับ แต่ภาษาอื่น ๆ เขาก็มีฟีเจอร์นี้กันมาชาตินึงได้แล้วล่ะ (C#4.0, Scheme, Clojure, และอื่น ๆ ) ส่วนภาษายอดนิยมอย่าง Java นั้นจะมีฟีเจอร์นี้เข้ามาในเวอร์ชั่น 8 ครับ (ซึ่งออกตัว RC1 มาแล้ว)

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *

This site uses Akismet to reduce spam. Learn how your comment data is processed.