Infinite List

การประกาศ List นอกจากจะทำได้ตามวิธีปรกติแล้ว Haskell ยังทำท่านี้ได้อีกด้วย

    ghci> [1..10]
    [1,2,3,4,5,6,7,8,9,10]
    ghci> ['a'..'z']
    "abcdefghijklmnopqrstuvwxyz"

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

    ghci> [1,3..20]
    [1,3,5,7,9,11,13,15,17,19]
    ghci> ['a','f'..'z']
    "afkpuz"

สมาชิกตัวสุดท้ายที่บอกขอบเขตบนนั้น อาจอยู่หรือไม่อยู่ใน List ที่สร้างเสร็จก็ได้ แต่จะไม่มีสมาชิกที่มีค่าใหญ่กว่านี้ครับ

ส่วนการประกาศค่าแบบลดลงนั้น ไม่สามารถบอกแค่ [10..1] ได้ ต้องบอกสมาชิกตัวถัดมาด้วย

    ghci> [10,9..1]
    [10,9,8,7,6,5,4,3,2,1]

หมายเหตุว่าให้ระวังการประกาศ List แบบนี้กับเลขทศนิยม เพราะ bug ของตัว Haskell เองทำให้อาจมีสมาชิกที่ใหญ่กว่าขอบเขตบนโผล่มาได้ครับ

อนึ่ง เราสามารถละส่วนขอบเขนบนได้ โดย Haskell จะเอาขอบเขตบนของ type นั้นมาเติมให้เอง เช่น

    ghci> ['a','z'..]

ปล่อยไว้ซักพัก จะเห็นว่า List ที่สร้างขึ้นนี้วิ่งไปจบที่ตัวอักษรซักตัวเอง และถ้าสงสัยว่าขอบเขตบนนั้นคืออะไร ก็สามารถหาได้โดย

    ghci> maxBound :: Char
    '\1114111'
    ghci> minBound :: Char
    '\NUL'

แต่สำหรับข้อมูลบาง type ที่ไม่มีขอบเขตบน (เช่น Integer) เมื่อสั่ง [1..] ตัวเลขจะวิ่งขี้นไปเรื่อยๆ ไม่มีที่สิ้นสุดครับ (เป็น List ที่ยาวเป็นอนันต์)


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

ฟังก์ชันที่ใช้เพื่อขอสมาชิกเพียงไม่กี่ตัวแรกของ List จากทางด้านหน้าคือ take

    ghci> take 10 [1,3..]
    [1,3,5,7,9,11,13,15,17,19]

ส่วน drop นั้นจะใช้เพื่อทิ้งสมาชิกตัวหน้าออกไป

    ghci> drop 5 [1..10]
    [6,7,8,9,10]

(drop เพียงอย่างเดียวนั้นไม่ค่อยมีประโยชน์กับ List ที่ยาวเป็นอนันต์ครับ)

ฟังก์ชันอีกกลุ่มที่ทำหน้าที่เช่นเดียวกันนี้ เพียงแต่เปลี่ยนจากจำนวนสมาชิกที่ต้องการแน่นอน ไปเป็นการตรวจสอบค่าสมาชิกว่าตรงเงื่อนไขหรือยัง คือ takeWhile และ dropWhile

    ghci> takeWhile (<10) [1,3..20]
    [1,3,5,7,9]
    ghci> dropWhile (/=3) [1,2,3,4,5,4,3,2,1]
    [3,4,5,4,3,2,1]

ย้ำอีกทีว่าฟังก์ชันพวกนี้จะเริ่มทำงานจากด้านหัว และคืนค่าทันทีเมื่อเงื่อนไขครบ เห็นได้จาก dropWhile (/=3) ที่ทิ้งเลข [1,2] เฉพาะทางด้านหัวเท่านั้น


นอกจากจะใช้การประกาศ List ตามด้านบนเพื่อสร้าง List ที่ยาวอนันต์ได้แล้ว ยังสามารถสร้าง List ที่ยาวเป็นอนันต์โดยใช้ฟังก์ชันเหล่านี้

cycle รับตัวแปรเป็น List ที่จะเอามาทำซ้ำเรื่อยๆ

    ghci> take 15 (cycle "kak ")
    "kak kak kak kak"

ส่วน repeat จะรับตัวแปรคือสมาชิกตัวเดียวของ List ที่จะเอาไปทำซ้ำๆ แต่ถ้ารู้ขนาดที่แน่นอนอยู่แล้ว สามารถใช้ฟังก์ชัน replicate แทนได้

    ghci> take 3 (repeat 5)
    [5,5,5]
    ghci> replicate 3 5
    [5,5,5]

Nattawut Phetmak

Jack of all Trades

blog comments powered by Disqus