เอิร์ลแลง(Erlang) ตอนที่ 5 List

posted on 09 Dec 2009 16:51 by roofimon in Erlang

Erlang Lists

ลิสท์ในเอิร์ลแลงเป็นที่สำหรับเก็บชุดของอิลิเมนท์คล้ายๆกับทัปเปิลแต่กระบวนการทำงานกับลิสท์นั้นแตกต่างจากทัปเปิลมากๆ ก่อนอื่นเรามาเริ่มจากวิธีการสร้างลิสท์ก่อน โดยเราสามารถสร้างลิสท์ได้โดยการใช้เครื่องหมาย [...] และแต่ละอิลิเมนท์จะถูกคั่นด้วยเครื่องหมายลูกน้ำ , โดยที่อิลิเมนท์ในลิสท์ม่จำเป็นจะต้องเหมือนกัน เช่น

[january, february, march]
[123, def, abc]
[a,[b,[c,d,e],f], g]
[]
[{person, 'Joe', 'Armstrong'}, {person, 'Robert', 'Virding'},
{person, 'Mike', 'Williams'}]
[72,101,108,108,111,32,87,111,114,108,100]
[$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d]
"Hello World"

ต่อไปเรามาดูคุณสมบัติของลิสท์เบื้องต้น [a,[b,[c,d,e],f], g] ลิสท์ตัวนี้จะมีขนาดความยาวเท่ากับ 3 โดยที่อิลิเมนท์ตัวแรกเป็นอะตอม a ตัวที่สองเป็นลิสท์ [b, [c,d,e],f] และตัวที่สามคืออะตอม g
ลิสท์ว่างเราสามารถถูกสร้างได้ด้วย [], นอกจากนี้เรายังสร้างลิสท์ของแทกทัปเปิลได้ [{person, 'Joe', 'Armstrong'}, {person, 'Robert',
'Virding'}, {person, 'Mike', 'Williams'}]

Erlang Characters and Strings

สำหรับเอิร์ลแลงแล้วมันเองเป็นภาษาที่ไม่มีข้อมูลประเภทสตริง ตัวอักษรในเอิร์ลแลงที่ถูกนำเสนอออกมานั้นเบื่องหลังมันคือตัวเลข และการนำค่าตัวเลขที่ใช้แทนตัวอักษรใดๆสามารถทำได้ด้วยการใส่เครื่องหมาย $ ไว้ที่หน้าตัวอีกษรนั้นๆเช่น
1> $A.
65
2> $A + 32.
97
3> $a.
97
ดังนั้นเราจะพบว่าในเอิร์ลแลงจะไม่มีการใช้ข้อมูลที่เป็นประเภทสตริง เพราะจริงๆแล้วสตริงคือลิสท์
ของ ASCII ที่ถูกแสดงผลภายใต้เครื่องหมาย '...' ดังนั้นถ้าเราเขียนสตริง "Hello World" นั่นหมายความว่า
เรากำลังสร้างลิสท์ที่มีค่า [72,101,108,108,111,32,87,111,114,108,100]
4> [65,66,67].
"ABC"
5> [67,$A+32,$A+51].
"Cat"
6> [72,101,108,108,111,32,87,111,114,108,100].
"Hello World"
7> [$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d].
"Hello World"

Erlang Atoms and Strings
หลายคนอาจเริ่มสงสัยว่าแล้วอะตอมกับสตริงต่างกันอย่างไร อย่างแรกเลยคือข้อมูลทั้งสองอย่างถูกโพรเซสต่างกัน
สำหรับอะตอมสิ่งที่เราสามารถทำได้กับมันคือการเปรียบเทียบ ในทางกลับกันสำหรับสตริงแล้วเราสามารถทำงานกับมันได้หลาย
รูปแบบมาก เช่นสตริง "Hello World" นั้นสามารถแยกมันออกเป็นส่วน เช่น ["Hello", "World"]

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

Erlang Building and Processing Lists
เราได้รู้จากหัวข้อก่อนหน้านี้แล้วว่าสิ่งที่ทำให้ลิสท์กับทัปเปิลแตกต่างกันก็คือการโพรเซสนั้นเองโดยที่ทัปเปิลนั้นเราสามารถทำได้เพียงดึงเอาอิลิเมนท์จากตำแหน่งที่เราต้องการออกมาเท่านั้น  ในขณะที่ลิสท์นั้นเราจะแบ่งข้อมูลในลิสท์ออกเป็นสองชุดคือหัวกับหาง โดยที่หัวคืออิลิเมนท์ตัวแรกในลิสท์และส่วนที่เหลือจะหลายเป็นหาง โดยที่เราสามารถแตกอิลิเมนท์ออกไปได้เรื่อยๆจนกว่าลิสท์นั้นจะว่าง ดังนั้นถ้าเรามีลิสท์ [1,2,3] เราจะได้หัวที่มีค่าเป็น 1 และหางเป็น [2,3] เมื่อเราเรียนรู้เกี่ยวกับเอิร์ลแลงเราจำเป็นต้องรู้การประกาศลิสท์แบบรีเคอร์สีฟเพราะถ้าเราไม่เข้าใจตั้งแต่แรกมันจะทำให้เรา งง เป็นอย่างมากดังนั้นให้ดูการประกาศลิสท์ทั้งหมดด้านล่าง ว่าผลที่ได้ออกมามีค่าเท่ากันหมด

[one, two, three, four]
[one, two, three, four|[]]
[one, two|[three, four]]
[one, two|[three|[four|[]]]]
[one|[two|[three|[four|[]]]]]

ข้อควรระวังคือเราต้องมีอิลิเมนท์ที่ฝั่งซ้ายมือของเครื่องหมาย cons และลิสท์ทางขวามือที่อยู่ในวงเล็บปีกแข็งเดียวกัน ด้วยการใช้
ข้อกำหนดนี้เราสามารถสร้างลิสท์ที่อยู่ในลักษณะที่ถูกต้องได้(ไม่ใช่ข้อกำหนดแต่ควรทำ)อย่างไรก็ตามลิสท์ในเอิร์ลแลงก็ไม่จำเป็นจะต้องเป็นจะต้องเป็นไปตามข้อกำหนดข้างต้นที่ อิลิเมนท์ด้านขวามือจะต้องเป็นลิสท์ก็ได้ยกตัวอย่างเช่นให้ลองพิมพ์  [[1, 2]|3] ลงไปในเชลแล้วดูผลที่ได้ก็คือลิสท์ที่เป็นแบบไม่ถูกลักษณะนั่นเอง ลิสท์ในลักษณะนี้เหมาะสำหรับงานประเภทเร่งรีบและรีบเร่งซึ่งเราจะกล่าวถึงเรื่องนี้ในภายหลัง  แต่อย่างไรก็ตามเราควรหลีกเลี่ยงการใช้ลิสท์ในลักษณะที่ผิดเนื่องจากถ้าเราใช้งานมันเยอะเข้า เราอาจจะแยกไม่ออกว่า เราตั้งใจจะใช้มันหรือมันเกิดจากความผิดพลาดของโปรแกรม เช่น ถ้าเราพิมพ์ [2|3] แทนที่ [2|[3]], โปรแกรมของเราสามารถถูกคอมไพล์ได้อย่างไม่มีปัญหาแต่อย่างไรก็ตามโปรแกรมส่วนนี้จะเกิดปัญหาในกรณีที่เราพยายามจะทำงานกับส่วนหางของลิสท์โดยที่เราคิดว่ามันเป็นลิสท์อย่างที่มันควรจะเป็นโดยที่แท้จริงแล้วมันเป็นอะตอม

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

ลิสท์เองก็เตรียมโอเปอร์เรชั่นหลายๆอย่างมาให้เราใช้งานได้เลยดังตัวอย่าง โดยที่โอเปอร์เรชั่นเหล่านี้ไม่ใช้ BIFs ดังนั้นการเรียกใช้งานจำเป็นต้องใช้เครื่องหมาย : คั่น
1> lists:max([1,2,3]).
3
2> lists:reverse([1,2,3]).
[3,2,1]
3> lists:sort([2,1,3]).
[1,2,3]
4> lists:split(2,[3,4,10,7,9]).
{[3,4],[10,7,9]}
5> lists:sum([3,4,10,7,9]).
33
6> lists:zip([1,2,3],[5,6,7]).
[{1,5},{2,6},{3,7}]
7> lists:delete(2,[1,2,3,2,4,2]).
[1,3,2,4,2]
8> lists:last([1,2,3]).
3
9> lists:member(5,[1,24]).
false
10> lists:member(24,[1,24]).
true
11> lists:nth(2,[3,4,10,7,9]).
4
12> lists:length([1,2,3]).
** exception error: undefined function lists:length/1
13> length([1,2,3]).
3
แต่อย่างที่บอกว่าโอเปอร์เรชั่นเหล่านี้ไม่ใช่ BIFs ยกเว้น length
เครื่องหมาย ++ และ--, มีไว้สำหรับรวมลิสท์และแตกลิสท์ออกดังตัวอย่าง
1> [monday, tuesday, Wednesday].
[monday,tuesday,wednesday]
2>
2> [1|[2|[3|[]]]].
[1,2,3]
3> [a, mixed, "list", {with, 4}, 'data types'].
[a,mixed,"list",{with,4},'data types']
4> [1,2,3] ++ [4,5,6].
[1,2,3,4,5,6]
5> [1,2,2,3,4,4] -- [2,4].
[1,2,3,4]
6> "A long string I have split "
6> "across several lines.".
"A long string I have split across several lines."
เครื่องหมาย ++ มีไว้เพื่อรวมลิสท์สองตัวเข้าด้วยกันเป็นลิสท์ตัวใหม่เช่น [1,2]++[3,4] จะได้ [1,2,3,4]
ส่วนเครื่องหมาย -- มีไว่สำหรับแยกอิลิเมนท์ที่เรากำหนดไว้ที่ด้านขวามือ ออกมาจากลิสท์ทางซ้ายมือ
ดังนั้น [1,1] -- [1] จะได้[1], ส่วน[1,2,3,4] -- [1,4] จะได้[2,3]. แต่ถ้าเราใช้ [1,2] -- [1,1,3], ผลจะออกมาเป็น [2]. ถ้าอิลิเมนท์ทางขวามือไม่มีในด้านซ้ายมันจะถูกมองข้ามไป
ทั้ง ++ และ-- จะทำงานแบบอิงด้านขวาเป็นหลัก(right-associative),

7> [1,2,3]--[1,3]--[1,2].
[1,2]
8> ([1,2,3]--[1,3])--[1,2].
[]

สุดท้ายถ้าเราพิมพ์ "Hello " "Concurrent " "World" เราจะได้ "HelloConcurrentWorld".
อย่างไรก็ตามเราสามารถเพิ่มอิลิเมนท์เข้าไปในลิสท์ได้สองวิธีคือ
• ใช้ cons ตรงๆ [1|[2,3,4]].
• หรือใช้ ++ แทน [1] ++ [2,3,4].
การทำงานทั้งสองแบบให้ผลเหมือนกันแต่ ++ จะทำงานได้ช้ากว่าดังนั้นในกรณีที่เราต้องการเพิ่มอิลิเมนท์เข้าไปที่
หัวของลิสท์ใช้ cons จะทำได้เร็วกว่า

 

edit @ 9 Dec 2009 16:54:57 by cyber-climber

edit @ 15 Dec 2009 11:07:04 by cyber-climber

Comment

Comment:

Tweet

BIFs built-in functions อยู่ดีดีก็โผล่มาเล่นเอางงเลย ^ ^

#2 By nuboat (180.210.216.68) on 2010-01-10 17:45

เรื่อง list พอเข้าใจครับ พอเอามารวมๆ กันเริ่มงง เลยต้องอ่านอีกรอบและหลายรอบ

#1 By somkiat on 2009-12-10 00:57