/ 365วันแห่งโปรแกรม

[365 วันแห่งโปรแกรม #day66] Filter Map Reduce (ตอนที่ 2)

วันที่หกสิบหกของ ‪#‎365วันแห่งโปรแกรม และแล้วเราก็เข้าสู่ช่วง 300 วันสุดท้ายครับ >< วันนี้ก็ต่อจากเมื่อวานเลย เป็นเรื่อง Map ครับ


Map

Map เป็นคำสั่งสำหรับใช้กระทำอะไรบางอย่างกับข้อมูลทุกตัวใน collection (list) เช่น เพิ่มค่าให้ทุก element อีก 2 จัด format ให้ element ทุกตัวใหม่ อะไรประมาณนี้ครับ สรุปง่ายๆ คือ Map ใช้สำหรับแปลงรูปแบบของข้อมูลทุกตัวให้เป็นรูปแบบที่เราต้องการ

Map จะรับพารามิเตอร์เป็น collection และฟังก์ชันสำหรับแปลงข้อมูล ปริมาณของข้อมูลขาเข้าและข้อมูลขาออกจะเท่ากันเสมอ เนื่องจาก Map จะไม่มีการตัดข้อมูลใดๆ ออกไปเลย

กำหนดให้เซ็ต s มีข้อมูลเป็นจำนวนเต็มตั้งแต่ 1 ถึง 100 จงเพิ่มค่าของข้อมูลทุกตัวเป็น 2 เท่า

โจทย์นี้บอกเราว่าไม่ว่า input จะเป็นอะไรให้คูณด้วย 2 เลย สามารถเขียนเป็นสัษลักษณ์ทางคณิตศาสตร์ได้แบบนี้ครับ

$s = \{ 1, 2, 3,..., 100 \}$

$y = \{ 2x | x \epsilon s \}$

Syntax นี้ใกล้เคียงกับ List comprehension ใน Haskell มาก ดังนั้นเราจะลองเขียนใน Haskell ดูก่อน

Haskell

s = [1..100]
[2*x|x<-s]

ดูไม่เป็นโปรแกรมมิ่งเลย >< งั้นลองใช้ฟังก์ชัน Map ดูดีกว่าครับ

Haskell

s = [1..100]
double x = x * 2
map double s

เพื่อให้เข้าใจมากขึ้นเราจะลองทำในภาษาแบบ Imperative ครับ

เหมือนกับ Filter คือเราต้องรับฟังก์ชันเข้ามาเพื่อประมวลผล ดังนั้นก็หนีไม่พ้น Strategy pattern ครับ

สร้าง Strategy Interface ก่อน

C#

public interface IMapStrategy<I,O>
{
    O Map(I item);
}

เนื่อจากว่าในฟังก์ชัน Map นี้ชนิดของ input และชนิดของ output สามารถเป็นคนละอันกันได้ ดังนั้นผมจึงทำ Strategy Interface ที่มี Generic 2 ตัว คือ I และ O แทน input type และ output type ตามลำดับครับ

ต่อไปสร้าง Concrete Strategy

C#

public class DoubleValueMap : IMapStrategy<int, int>
{
    public int Map(int item)
    {
        return item * 2;
    }
}

สร้างฟังก์ชัน Map

C#

public IEnumerable<O> Map<I, O>(IMapStrategy<I, O> strategy, IEnumerable<I> list)
{
    foreach (var item in list)
    {
        yield return strategy.Map(item);
    }
}

ในฟังก์ชัน Map เราจะไม่มีเงื่อนไขแบบของ Filter ครับ เพราะเราไม่ต้องกรองอะไรเลย ทำแค่ apply ฟังก์ชันของ strategy เท่านั้น

รัน!!

C#

var s = Enumerable.Range(1, 100).ToList();
var result = Map(new DoubleValueMap(), s);
PrintCollection(result);

เสร็จแล้วครับ ><

เรามาดูใน Linq และ Java 8 กันบ้าง ว่าชีวิตจะง่ายขึ้นแค่ไหน

C# + Linq

var s = Enumerable.Range(1, 100).ToList();
var result = from x in s select x * 2;
PrintCollection(result);

ใน select clause ของ Linq เราสามารถจัดค่าที่ดึงออกมาใหม่ได้ โอ้ว ทำไมมันง่ายขนาดนี้ ><

Java 8

List<Integer> s = IntStream.range(1, 101).boxed().collect(Collectors.toList());
List<Integer> result = s.stream().map(i -> i * 2).collect(Collectors.toList());
printCollection(result);

Java 8 เขียนยาวกว่าหน่อย แต่ก็ไม่ได้ยุ่งยากนัก

โดยรวมแล้วถือว่าชีวิตดีขึ้นครับ เมื่อภาษาโปรแกรมมี Feature พร้อมใช้ อะไรๆ ก็ดีไปหมด ต่างจากตอนเขียนเองที่ต้องติดหนี้ (Technical Debt) ไปเรื่อยๆ

#‎day66 #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...