Bill Inmon กับ Ralph Kimball

posted on 09 Aug 2011 11:34 by roofimon

เรื่องมีอยู่ว่าต้องเตรียมการอะไรบางอย่างสำหรับงานสัมมนาเรื่อง Pentaho ก็เลยอยากจะเกริ่นนำไปเรื่อง Data Warehouse และแน่นอนเรื่องหนึ่งที่น่าสนใจเกียวกับ Data Warehouse คือการต่อสู้กันระหว่างสองขั้วความคิด (Two School Of Thought) นั่นคือเรื่องราวของ Raph Kimball (Star Schema with Share Dimentional) กับ Bill Inmon (Enterprise Data Warehouse, Cooperate Information Factory) สองศาสดาแห่ง Data Warehouse 

คำถามคือเราจะเลือกเริ่มทำงาน ด้วยแนวคิดไหนเป็นหลักระหว่างการทำ Enterprise Data Warehouse หรือการทำ Star schema database with fact tables sharing conformed dimensions? สิ่งแรกเลยเราควรเข้าใจก่อนว่า Enterprise Data Warehouse หรือ Cooperate Information Factory นั้นคือสถานที่เก็บข้อมูลทั้งหมดขององค์กรโดยที่โครงสร้างข้อมูลจะถูกออกแบบ ให้อยู่ในรูปของ Third Normal Form หลักจากนั้นเมื่อมีความต้องการใช้ข้อมูล สิ่งที่ต้องทำคือการทอนขนาดของ Warehouse ลงให้เหลือเท่ากับสิ่งที่ต้องการใช้และดึงข้อมูลออกไปเก็บไว้ ดังนั้นจะเห็นว่า Data Mart ของ EDW คือตัวย่อขนาดของ EDW นั่นเอง และการบวนการสร้าง EDW ที่ Inmon เสนอนั้นเรามักรู้จักกันในนามของ Top-Down Approach คือการสร้างของใหญ่ก่อนแล้วค่อยๆสร้าง Data Mart ในภายหลังวิธีการนี้ใช้เวลาการสร้างที่ยาวนานและลงทุนสูงแต่สิ่งที่ได้คืน กลับมาคือ ข้อมูลที่มีความถูกต้องเที่ยงตรงในเชิงเวลา(Time Variant) สูงมาก

ส่วน Star Schema Approach นั้นจะคิดอีกด้านหนึ่งนั่นคือโครงสร้างข้อมูลจะถูกเก็บในรูปแบบของ Fact Table  และข้อมูลใน Fact Table จะถูกโยงออกมาพบกับ Dimensional Table โดยวิธีการออกแบบจะไม่ใช้หลัการ Normalization แต่จะทำการ Denormailize แทนเพื่อลดความซับซ้อนของ Query ซึ่งจะส่งผลให้ Business User สามารถเข้ามาใช้ข้อมูลได้อย่างสะดวกมากขึ้นนอกจากนี้ จะมีไม่การทำสิ่งที่เรียกว่า Centralize Data เหมือน EDW แต่จะทำการสร้างชุดของ  Star Schema ขนาดเล็กที่สามารถตอบโจทย์ของแต่ละ Business Domain(โดยสิ่งนี้เรียกว่า Data Mart) ไว้เป็นจำนวนมาก นั่นคือที่มาของคำว่า Kimball Bus นั่นเอง ดังนั้นการสร้าง Data Warehouse Bus จะเป็นการทำแบบ Bottom Up นั่นเองคือสร้าง Data Mart จาก Business Process ที่ต้องการก่อนแล้วจากนั้นค่อยๆประกอบมันเข้าด้วยกันเป็น Warehouse ขนาดใหญ่ กระบวนการนี้ใช้เวลาน้อยกว่าเงินน้อยกว่าแต่มักจะจบลงด้วยการมี Silo เป็นจำนวนมากและอาจเกิดความไม่เที่ยงตรงได้ถ้าไม่มีการทำ Data Governance ที่ดี

เอาคร่าวๆเท่านี้ก่อนนะครับ ท่านใดมีข้อมูลที่ต้องการเสริมขอเชิญได้นะครับ -/\-

Functions
ใน Erlang นั้นฟังก์ชั่นจะขึ้นต้นด้วยอะตอมที่ใช้เป็นชื่อของฟังก์ชั่น โดยที่ส่วนหัวของฟังก์ชั่นจะประกอบไปด้วยชื่อและคู่ของวงเล็บที่ครอบพารามิเตอร์ที่ต้องการส่ง (มากว่าศูนย์ตัว) โดยที่พารามิเตอร์ที่ส่งเข้าไปในฟังก์ชั่นนั้นจะมีชื่อเรียกว่า arity หลังจากนั้นเราจะใช้เครื่องหมาย ( -> ) เพื่อระบุว่าเรากำลังจะเข้าสู่เนื้อของฟังก์ชั่น ตามตัวอย่างที่ 2-1 ใช้แสดงฟังก์ชั่นของ Erlang ที่สร้างขึ้นมาเพื่อคำนวณพื้นที่ของรูปร่างต่างๆ โดยที่ในเนื้อของฟังก์ชั่นถ้าเรามีการไปเรียกหรือทำงานมากกว่าหนึ่งอย่างเราก็สามารถทำได้ด้วยการแยกส่วนเหล่านั้นด้วยเครื่องหมาย , และการแยกแพทเทิร์นที่ต่างกันของฟังก์ชั่นสามารถทำได้ด้วยการใส่เครื่องหมาย ; และเมื่อเราต้องการจบฟังก์ชั่นเราต้องจบมันด้วยเครื่องหมาย .  
    area({square, Side}) ->
        Side * Side ;
    area({circle, Radius}) ->
        math:pi() * Radius * Radius;
    area({triangle, A, B, C}) ->
        S = (A + B + C)/2,
        math:sqrt(S*(S-A)*(S-B)*(S-C));
    area(Other) ->
            {error, invalid_object}.
เมื่อฟังก์ชั่นถูกเรียกมันจะทำการวิ่งหาแพทเทิร์นที่เหมาะสมถ้ามันพบแพทเทิร์นที่เหมาะสมมันก็จะทำการส่งพารามิเตอร์ที่เข้าไปในส่วนของฟังก์ชั่นนั้น โดยที่มีหลักการการเขียนแพทเทิร์นที่ดีคือทุกแพทเทิร์นจะต้องระบุพารามิเตอร์ให้เหมาะกับแพทเทิร์นที่ถูกสร้างและไม่รบกวนกับแพทเทิร์นอื่น เพื่อที่เราจะได้กำหนดแพทเทิร์นสุดท้ายสำหรับจัดการกับการเรียกใช้ที่ไม่เข้ากับแพทเทิร์นใดๆเลย จากตัวอย่าง 2-1 ฟังก์ชั่น area จะทำงานเมื่อเราส่งแพทเทิร์น square, circle และ triangle มาและจะส่งค่าทัปเปิ้ล {error, invalid_object} ในกรณีที่เราส่งแพทเทิร์นอื่นเข้ามา
    area({circle, 2}).
เมื่อเราทำการเรียกฟังก์ชั่นในลักษณะนี้มันจะวิ่งไปแมปกับแพทเทิร์นแรกก่อนซึ่งผลคือจะไม่ผ่านเพราะ circle ไม่เท่ากับ square มันจึงทะลุลงไปที่แพทเทิร์นที่สองและทำการเปรียบเทียบซึ่งจะตรงทั้งแพทเทิร์นจำนวนพารามิเตอร์ดังนั้นตัวฟังก์ชั่นบริเวณนั้นจะเริ่มทำงาน math:pi()*2*2, ผลจะได้หลังจาการป้ดเศษ 12.57 และเมื่อมีการพบแพทเทิร์นที่เหมาะสมแล้วนั่นหมายความว่าแพทเทิร์นที่เหลือจะไม่ถูกเปรียบเทียบและสำหรับคลอสสุดสุดท้าย area(Other)-> ... มีไว้สำหรับจับคลอสที่เหลือจากสามคลอสที่อยู่ด้านบนซึ่งจะโยนเออร์เร่อร์ออกมา
    {error, invalid_object}.
ต่อไปเราจะลองสลับข้างเอาคลอสอื่นขึ้นมาก่อน ดังนั้นผลที่ได้คือการเปรียบเทียบจะตกลงไปที่ Other ทั้งหมด {error, unknown_shape}, เพราะการเปรียบเทียบกับ Other จะสำเร็จตลอดเวลา
    flatten(Other) -> {error, unknown_shape};
    flatten(cube) -> square;
    flatten(sphere) -> circle.
ต่อไปเป็นตัวอย่างการเขียนโปรแกรมฟังก์ชั่น factorial
    factorial(0) -> 1;
    factorial(N) ->
        N * factorial(N-1).
ถ้าเราเรียก factorial(3) การเปรียบเทียบแพทเทิร์นครั้งแรกกับ factirial(0) จะไม่สำเร็จทำให้ระบบจะทำการวิ่งลงไปหาคลอสที่สองนั่นคือ factorial(N) ซึ่งจะสำเร็จและจะผูก N เข้ากับ 3 ซึ่งคลอสนี้จะส่ง 3*factorial(2) ออกมา
สิ่งที่ระบบจะทำต่อไปคือเรียก factorial(2) ซึ่งผลที่ได้จากการเรียกคือ 2*factorial(1) ต่อไปมันก็จะเรียก 1*factoial(0) ที่น่าสนใจคือ factorial(0) จะส่งค่า 1 กลับและทำการจบการเรียกเพราะพบเครื่องหมาย ; เราสามารถเขียนเป็นชั้นๆเพื่อให้เห็นภาพได้ดังนี้
    factorial(3).
    Level 1: 3 * factorial(3 - 1) (returns 6)
    Level 2: 2 * factorial(2 - 1) (returns 2)
    Level 3: 1 * factorial(1 - 1) (returns 1)
    Level 4: 1 (returns 1)

Modules
ฟังก์ชั่นจะถูกจับกลุ่มกันเป็นสิ่งที่เรียกว่าโมดูล โดยที่โมดูลนั้นจะต้องถูกจัดไว้ในไฟล์ที่มีนามสกุล .erl และชื่อไฟล์กับชื่อโมดูลจะต้องตรงกันเท่านั้นและชื่อโมดูลจะต้องเป็นไปตามมาตรฐาน -module(Name) ตามตัวอย่าง
ที่เราจะสร้างโมดูล demo ในไฟล์ demo.erl
    -module(demo).
    -export([double/1]).
        % This is a comment.
        % Everything on a line after % is ignored.
        double(Value) ->
           times(Value, 2).
        times(X,Y) ->
            X*Y.
โดยที่คำสั่ง export นั้นจะเป็นตัวที่บอกว่าฟังก์ชั่นไหนจะถูก export ตามมาตรฐาน Function/Arity ฟังก์ชั้นนี้เป็น Global ฟังก์ั่ชั่นนั่นแสดงว่ามันสามารถถูกเรียกได้จากข้างนอกโมดูลได้ตัวอย่างการเรียกใช้ฟังก์ชั่นในดมดูลสามารถทำได้ดังนี้เช่น demo:double(2) และผลที่ได้คือ 4. สำหรับฟังก์ชั่นที่เป็นโลคอลนั้นจะถูกเรียกได้เฉพาะในโมดูลนี้นั้นๆเท่านั้น 
วิธีการระบุตัวตนของฟังก์ชั่นใน Erlang จะทำผ่านชื่อ, จำนวนพารามิเตอร์และโมดูลที่เป็นเจ้าของมัน โดยที่ฟังก์ชั่นในโมดูลเดียวกันอาจจะมีชื่อเหมือนกันแต่มีจำนวนพารามิเตอร์ไม่เท่ากันก้ได้ และในการใช้งานเราไม่จำเป็นต้องเรียงลำดับว่าต้องสร้างฟังก์ชั่นก่อนแล้วถึงจะเรียกใช้งานมันได้ สำหรับ Erlang แล้วเราสามารถสลับตำแหน่งให้การสร้างฟังก์ชั่นไว้หลังการเรียกใช้ได้
หลังจากที่เราทำการสร้างโมดูลแล้วส่วนต่อไปคือเราต้องคอมไพล์เพื่อนำไปใช้งาน สำหรับ Erlang แล้วการคอมไพล์จะทำให้เราได้ไฟล์อีกหนึ่งไฟล์ที่ชื่อเหมือนโมดูลแต่นามสกุลต่างกัน (module.beam) ต่อไปเราลองมาใช้ความสามารถเบื้องต้นของ Erlang Shell กันโดยให้เปิดมันขึ้นมาที่ไดเรคทอรี่ที่เราเก็บ source code ของเราไว้ โดยเราสามารถย้ายตำแหน่งไปมาได้เหมือนอยู่บน OS Shell ด้วยการใช้คำสั่ง cd(Directory) และเมื่อเราย้ายไปยังไดเรคทอรี่ที่เราต้องการแล้วเราก็สามารถคอมไฟล์โปรแกรมที่เราต้องการได้เลยด้วยการใช้คำสั่ง c(Module) ที่เราต้องการ
ในระบบ Erlang ขนาดใหญ่นั้นจะประกอบไปด้วยโมลูลจำนวนมาก ซึ่งทั้งหมดจะถูกคอมไพล์ด้วยมาตรฐานเดียวกันหมด ซึ่งผลที่ได้จากการคอมไพล์คือไฟล์ .beam ที่มี byte code อยู่ภายในและเราสามารถเรียกใช้มันได้และในการเรียกใช้ฟังก์ชั่นเราต้องอ้างถึงชื่อเต็มของฟังก์ชั่นนั้นๆ เนื่องจากเรากำลังพยายามเรียกใช้มันจากนอกโมดูล
    1> cd("/home/francesco/examples").
        /home/francesco/examples
        ok
    2> c(demo).
        {ok,demo}
    3> demo:double(10).
        20
    4> demo:times(1,2).
    ** exception error: undefined function demo:times/2

Module Directives
จากตัวอย่างที่ผ่านมาเราจะเห็นว่าโมดูลทุกตัวจะมีส่วนประกอบอีกตัวที่ชื่อว่า แอททริบิวท์(Attribute) โดยมันจะถูกเขียนอยู่ในลักษณะ -attribute(Value) โดยที่มันมักจะถูกเขียนไว้ที่ส่วนหัวของโมดูล สำหรับแอททริบิวท์ที่บังคบว่าจะต้องมีเสมอคือ -module เพื่อใช้บอกชื่อของโมดูลนั้นและตามมาด้วย -export ที่ใช้สำหรับการแสดงรายละเอียดของฟังก์ชั่นเช่น function/arity
A useful directive when programming is the –compile(export_all) directive, which at compile time will export all functions defined in the module. Another way of doing this
is to specify an option on compiling the file:
โมดูลไดเรคทีฟอีกตัวที่เป็นประโยชน์คือ -compile(export_all) ซึ่งมันจะทำการ export ทุกฟังก์ชั่นที่มีในโมดูลออกมาในจังหวะที่ทำการคอมไพล์ ซึ่งอีกทางที่เราสามารถทำได้คือการระบุออปชั่นไว้จังหวะที่เราทำการสั่งคอมไพล์ 
    c(Mod,[export_all]).
แต่สำหรับคำสั่งนี้เหมาะสำกรับการ test เท่านั้น ไม่ควรใช้บนงานแบบ production ต่อไปเป็นอีกคำสั่งที่น่าสนใจคือ -import(Module, [Function/Arity,...]) เราใช้สำหรับการ import ฟังชั่นจากโมดูลอื่นที่เราต้องการมาใช้ในโมดูลของเรา ยกตัวอย่างเช่น –import(math,[sqrt/1]) ที่เราเคยใช้ไปก่อนหน้านี้แล้ว
    -import(math, [sqrt/1]).
    area({triangle, A, B, C}) ->
        S = (A + B + C)/2,
        sqrt(S*(S-A)*(S-B)*(S-C));
ในบางกรณีการใช้คำสั่ง import อาจทำให้เราสับสนได้ว่าฟังก์ชั่นที่เราเรียกใช้งานเป็นฟังก์ชั่น Local ดังนั้นในบางกรณีเราต้องตรวจสอบรายละเอียดของฟังก์ชั่นนั้นด้วยคำสั่ง Mod:module_info/0 หรือ Mod:module_info/1 และจาก shell ก็สามารถเรียก m(Module)
    5> demo:module_info().
        [{exports,[{double,1},{module_info,0},{module_info,1}]},
            {imports,[]},
            {attributes,[{vsn,[74024422977681734035664295266840124102]}]},
            {compile,[{options,[]},
            {version,"4.5.1"},
            {time,{2008,2,25,18,0,28}},
            {source,"/home/francesco/examples/demo.erl"}]}]
    6> m(demo).
            Module demo compiled: Date: February 25 2008, Time: 18.01
            Compiler options: []
            Object file: /home/francesco/examples/demo.beam
            Exports:
                double/1
                module_info/0
                module_info/1
                ok

Erlang Complex Data Structures

ในเอิร์ลแลง(Erlang)โครงสร้างข้อมูลที่ซับซ้อนหมายถึงการนำโครงส้างข้อมูลแบบพื้นฐาน ไม่ว่าจะเป็นอะตอม ลิสท์ ทัปเปิล และอื่นๆมาประกอบรวมกันเพื่อจำลองข้อมูลที่ซับซ้อนบางอย่าง เช่นเราต้องการสร้าง person ให้เป็นโครงสร้างที่ซับซ้อนที่ประกอบไปด้วย ชื่อ นามสกุล สัตว์เลี้ยง ความชื่นชอบ หรือ อื่นๆ จะได้ออกมาประมาณนี้
[{person,"Joe","Armstrong",
    [ {shoeSize,42},
        {pets,[{cat,zorro},{cat,daisy}]},
        {children,[{thomas,21},{claire,17}]}]},
 {person,"Mike","Williams",
    [ {shoeSize,41},
        {likes,[boats,wine]}]}
]

หรือถ้าเราต้องการสร้าง person ขึ้นมาแบบค่อยเป็นค่อยไปทีละขั้น ด้วยการใช้โครงสร้างพื้นฐานก็ทำได้แต่ผลที่ออกมาจะดูอ่านยากมาก

1> JoeAttributeList = [{shoeSize,42}, {pets,[{cat, zorro},{cat,daisy}]},
1> {children,[{thomas,21},{claire,17}]}].
        [{shoeSize,42},
            {pets,[{cat,zorro},{cat,daisy}]},
            {children,[{thomas,21},{claire,17}]}]
2> JoeTuple = {person,"Joe","Armstrong",JoeAttributeList}.
        {person,"Joe","Armstrong",
            [{shoeSize,42},
                {pets,[{cat,zorro},{cat,daisy}]},
                {children,[{thomas,21},{claire,17}]}]}
3> MikeAttributeList = [{shoeSize,41},{likes,[boats,wine]}].
        [{shoeSize,41},{likes,[boats,wine]}]
4> MikeTuple = {person,"Mike","Williams",MikeAttributeList}.
        {person,"Mike","Williams",
            [{shoeSize,41},{likes,[boats,wine]}]}
5> People = [JoeTuple,MikeTuple].
        [{person,"Joe","Armstrong",
            [{shoeSize,42},
                {pets,[{cat,zorro},{cat,daisy}]},
                {children,[{thomas,21},{claire,17}]}]},
         {person,"Mike","Williams",
            [{shoeSize,41},{likes,[boats,wine]}]}]

จ้อได้เปรียบอีกข้อของ Erlang คือเราไม่ต้องนั่งเสียเวลาจัดการกับเมมโมรี่ เองเหมือนกับภาษาซี กระบวนการจัดการเมมดมรี่จะถูกควบคุมโดยกาเบจคอลเลคชั่น คือวีเอ็มจะจัดการคืนเมมโมรี่เมื่อไม่มีการใช้งานเมมโมรี่ส่วนที่ถูกจองไว้ ตามระยะเวลาที่กำหนด

Erlang Pattern Matching
การเปรียบเทียบรูปแบบ(Pattern Matching)ใน Erlang ใช้สำหรับงานเช่น
• การกำหนดค่าให้ตัวแปร
• การควบคุมการทำงานของโปรแกรม
• การดึงค่าออกมาจากข้อมูลที่ถูกประกอบกันขึ้นมา(compound)

การเปรียบเทียบรูปแบบสามารถเขียนได้ในรูปนี้

    รูปแบบ(Pattern) = นิพจน์(Expression)

ตัว รูปแบบ(Pattern)นั้นจะประกอบไปด้วยตัวแปรที่ถูกผูก(bound)และตัวแปรที่ยัง ไม่ถูกผูก(unbound) รวมทั้งประเภทข้อมูลพื้นฐานต่างๆเช่น อะตอม อินทิเจอร์ และ สตริง 

    Double
    {Double, 34}
    {Double, Double}
    [true, Double, 23, {34, Treble}]

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

• การเปรียบเทียบสำเร็จ ส่งผลให้เราสามารถผูกค่าเข้ากับตัวแปรได้ และส่งค่านั้นกลับไป
• การเปรียบเทียบล้มเหลว ไม่มีก่ารผูกค่าเข้ากับตัวแปรใดๆเลย

แล้วเราจะรู้ได้อย่างไรว่าการเปรียบเทียบของเราสำเร็จหรือไม่? ก่อนอื่นเราต้องเข้าใจกระบวนการทำงานก่อน โดยที่การทำงานจะเริมจากการตรวจสอบนิพจน์ที่อยู่ทางขวามือก่อนที่จะทำการ เปรียบเทียบค่าของมันกับรูปแบบ(Pattern)ทางซ้ายมือ โดยจะมีกฎการเปรียบเทียบอยู่สี่ข้อคืด

• ทั้ง นิพจน์และตัวรูปแบบจะต้องมีลักษณะเหมือนกันเช่นทัปเปิลจะต้องมีจำนวนอิลิเมน ท์เท่ากันเท่านั้น ลิสท์ที่มีเทอมเป็น [X|Xs] จะต้องถูกเทียบกับลิสท์ที่ไม่เป็นลิสท์ว่างเท่านั้น 
• The literals in the pattern have to be equal to the values in the corresponding place in the value of the expression. 
• ตัวแปรที่ยังไม่ถูกผูกจะโดนผูกเข้ากับค่า ก็ต่อเมื่อกระบวนการเปรียบเทียบรูปแบบทำได้สำเร็จ
• ตัวแปรที่ถูกผูกแล้วจะต้องมีค่าเหมือนกับที่ที่มันผูกไว้ของนิพจน์ทางด้านขวา

ตัวอย่าง, ลองเขียน Sum = 1+2 โดยที่ตัวแปรที่ยังไม่ถูกผูกอย่าง Sum จะถูกใช้เพื่อผูกเข้ากับผลของการบวก 1 กับ 2 กระบวนการคือ Erlang จะบวก 1 กับ 2 ก่อนแล้วหลังจากนั้นจะไปตรวจดู Sum ว่าเป็นตัวแปรแบบที่ยังไม่ถูกผูกหรือไม่ถ้าใช่มันจะใส่ค่า 3 ให้กับ Sum เพราะเงือนไขการเปรียบเทียบรูปแบบ ถูกต้อง ในทางกลับกันถ้า Sum ถูกผูกแล้ว Sum จะไม่ถูกกำหนดค่าใหม่เพราะผลการเปรียบเทียบล้มเหลว กรณีเดียวที่การเปรียบเทียบจะสำเร็จโดยที่ Sum ถูกผูกแล้วก็คือ Sum จะต้องมีค่าเป็น 3 มาก่อนแล้วเท่านั้น
อีกตัวอย่าง

1> List = [1,2,3,4].
[1,2,3,4]

กรณีนี้การเปรียบเทียบจะสำเร็จ List จะมีค่าเป็น [1,2,3,4] 
ตัวอย่างต่อไป:

2> [Head|Tail] = List.
[1,2,3,4]
3> Head.
1
4> Tail.
[2,3,4]
กรณีนี้ก็สำเร็จเช่นกันเพราะไม่มีการเปรียบเทียบกับ ลิสท์ว่าง 
ตัวอย่างต่อไป

5> [Head|Tail] = [1].
** exception error: no match of right hand side value [1]
6> [Head|Tail] = [1,2,3,4].
[1,2,3,4]
7> [Head1|Tail1] = [1].
[1]
8> Tail1.
[]

เกิดอะไรขึ้นกับคำสั่งที่ 5 ? มันเหมือนจะไม่มีปัญหาอะไร แต่!!! [Head|Tail] ถูกผูกเข้ากับ [1,2,3,4] เรียบร้อยแล้วดังนั้นเมื่อเราพยายามจะถูกมันเข้ากับ [1] มันจะเอา [1] ไปเทียบกับ [1,2,3,4] และแน่นอนว่ามันไม่เท่ากันทำให้การเปรยบเทียบล้มเหลว แต่ถ้าเราใช้คำสั่งที่ 6 การเปรียบเทียบจะสำเร็จ ดังนั้นถ้าเราต้องการดึงเอาค่าออกมาจากลิสท์ [1] เราจำเป็นต้องใช้ลิสท์อีกตัวที่ยังไม่ถูกผูกค่าไว้

9> {Element, Element, X} = {1,1,2}.
{1,1,2}
10> {Element, Element, X} = {1,2,3}.
** exception error: no match of right hand side value {1,2,3}

เกิด อะไรถ้าตัวแปรที่เราใช้ชื่อซ้ำกันตามตัวอย่างที่ 9 ตัวแปรตัวแรกจะไม่ถูกผูกโดยทันทีแต่มันจะรอ ส่วนอิลิเมนท์ที่สองจะถูกผูกและจะสำเร็จเมื่อค่าที่ถูกเปรียบเทียบมีค่าเป็น 1 เท่านั้น โดยให้ดูคำสั่งที่ 10 ในกรณีที่การเปรียบเทียบไม่สำเร็จเพราะค่าไม่สอดคล้องกัน

11> {Element, Element, _} = {1,1,2}.
{1,1,2}

ต่อ ไปคือเรื่องสัญลักษณ์แบบเปิด(wildcard symbol), _ , ผลของมันคือมันจะถูกผูกเข้ากับอะไรก็ตามที่ยังไม่ถูกผูก ดังตัวอย่าง (นิยมใช้ในการดึงข้อมูลออกมาจากลิสท์ที่มีขนาดใหญ่มากและเราต้องการดึงบางอิ ลิเมนท์ออกมา)

12> {person, Name, Surname} = {person, "Jan-Henry", "Nystrom"}.
{person,"Jan-Henry","Nystrom"}
13> [1,2,3] = [1,2,3,4].
** exception error: no match of right hand side value [1,2,3,4]

วุ่นวาย ไหมครับ แล้วทำไมเราต้องใช้การเปรียบเทียบรูปแบบ? อันดับแรกคือเอาไว้กำหนดค่าให้ตัวแปร ใน Erlang ถ้าเราทำ Int = 1 การทำงานคือมันจะเปรียบเทียบค่าที่อยู่ในตัวแปร Int กันอินทิเจอร์ 1 ในกรณีที่ Int ยังไม่ถูกผูกมันจะถูกผูกเข้ากับค่าอะไรก็ตามที่อยู่ทางขวามือ นี่เป็นกระบวนการการกำหนดค่าให้ตัวแปรซึ่งจริงมันไม่ได้กำหนดค่าตรงๆแต่มัน จะทำการเปรียบเทียบรูปแบบตัวแปรนั่นเองนั่นเอง  ต่อไปให้เราเขียน Int = 1 และตามด้วย  Int = 1+0 ขั้นแรกทำการผูก Int เข้ากับ 1 หลังจากนั้นจะทำการบวก 0 เพิ่มเข้าไปกับ 1 แล้วทำการเปรียบเทียบค่าที่ได้กับค่าที่ผูกอยู่กับ Int ซึ่งแน่นอนมันจะเท่ากันทำให้ผลของการทำการเปรียบเทียบรูปแบบถูกต้อง ในทางกลับกันถ้าเราเปลี่ยนคำสั่งที่สองเป็น Int = Int +1 ผลที่ได้คือการเปรียบเทียบจะล้มเหลวเนื่องจาก 2 จะไม่เท่ากับค่าที่ผูกอยู่กับ Int ที่มีค่าเป็น 1

การเปรียบเทียบรูปแบบ ยังถูกนำไปใช้ในการควบคุมการทำงานของโปรแกรมได้อีกด้วย ซึ่งจะขอกล่าวถึงเรื่องนี้ในภายหลัง ดังนั้นเราควรมาทดสอบความรู้เรื่องการเปรียบเทียบรูปแบบกันเสียหน่อย

{A, A, B} = {abc, def, 123}

การเปรียบเทียบแบบแรกเราจะพิจรณาก่อนว่าทัปเปิลทั้งสองมีขนาดเท่ากันหรือไม่ ในที่นี้มันมีขนาดเป็นสามเท่ากันดังนั้นขั้นแรกสำเร็จ ต่อไปคือการเปรียบเทียบในระดับอิลิเมนท์ โดยที่ A = abc ก่อนแล้ว A ตัวที่สองจะถูกเปรียบเทียบกับ def ซึ่งจริงๆแลอ้วค่าเริ่มต้านของ A คือ abc ดังนั้นเมื่อเราพยายามเทียบกับ def ผลที่ได้จึงล้มเหลว
การเปรียบเทียบรูปแบบ [A,B,C,D] = [1,2,3] จะล้มเหลวถึงแม้ว่าทั้งสองฟากจะเป็นลิสท์เหมือนกันแต่จำนวนอิลิเมนท์ของทั้ง สองฝั่งนั้นไม่เท่ากันเพราะทางซ้ายมีสี่ตัวแต่ทางขวามีสามตัว แต่บางคนอาจจะสงสัยว่า แล้ว D ไม่ใช่ลิสท์ว่างหรือ? ความจริงคือเนื่องจากเครื่องหมายที่เราใช้แบ่งระหว่างอิลิเมนท์ C กับ D คือ , ไม่ใช่เครื่องหมาย cons ทำให้ D ไม่สามารถเป็นลิสท์ว่างได้ แต่ถ้าเราเขียนใหม่เป็น [A,B, C|D] = [1,2,3] การเปรียบเทียบรูปแบบจะสำเร็จ โดยที่ A,B,C จะมีค่าเป็น 1,2,3 ส่วน D จะถูกผูกเข้ากับลิสท์ว่างอัตโนมัติ เรามาลองอีกตัวอย่าง [A, B|C] = [1,2,3,4,5,6,7] ผลที่ได้คือ A กับ B จะมีค่าเป็น 1 และ 2 ตามลำดับส่วน C จะถูกผูกเข้ากับ [3,4,5,6,7] กรณีที่เราเขียน [H|T] = [] ผลที่ได้คือล้มเหลวเพราะเราไม่รู้จะเปรียบเทียบ H กับอะไรเนื่องจากอีกด้านเป็นลิสท์ว่าง สุดท้ายเรามาลองดึงเอาค่าออกมาจากข้อมูลแบบประกอบ(compound data types)

{A, _, [B|_], {B}} = {abc, 23, [22, 23], {22}}

ผลการเปรียบเทียบคือสำเร็จ A จะถูกผูกเข้ากับอะตอม abc ส่วน B จะถูกผูกเข้ากับอินทิเจอร์ 22

14> Var = {person, "Francesco", "Cesarini"}.
{person, "Francesco", "Cesarini"}
15 {person, Name, Surname} = Var.
{person, "Francesco", "Cesarini"}

เราตั้งใจจะดึงเอาค่าต่างๆในทัปเปิลที่ใช้ชื่อแทคว่า person ออกมาเก็บไว้ในทัปเปิลอีกตัวซึ่งผลที่ได้คือ Name จะได้ค่า "Francesco" ส่วน Surname จะได้ค่า "Cesarini" ในเรื่องการดึงค่าออกมาจากลิสท์หรือทัปเปิลนั้นที่นิพจน์ที่อยู่ทางซ้ายมือ ตัวแปรต่างๆสามารถขึ้นต้นได้ด้วย _ ซึ่งจะหมายถึง "ไม่สนใจ" เครื่องหมายนี้มักถูกใช้กับค่าที่เราไม่สนใจแต่ตัวมันเองมีพฤติกรรมเหมือน กับตัวแปรทั่วๆไปเช่นเราสามารถนำมันมาเปรียบเทียบ ตรวจสอบ และนำไปใช้งานได้ การใช้ตัวแปรแบบ "ไม่สนใจ" นี้ถือว่าเป็นข้อปฎิบัติที่ดีของการเขียนโปรแกรมบนErlang เนื่องจากคนที่มาอ่านโปรแกรมต่อจากเราจะได้แยกแยะได้ว่าบริเวณไหนที่เขาไม่ ต้องสนใจ ยกตัวอย่างเช่น

{A, _, [B|_], {B}} = {abc, 23, [22, 24], {22}}

จะเห็นว่า _ จะไม่ถูกผูกเข้ากับค่าอะไรเลยและมันก็ไม่สนใจด้วยว่าค่าอะไรที่เรากำลัง เปรียบเทียบอยู่ แต่ในทางกลับกันถ้าเราเขียนอีกแบบ

{A, _int, [B|_int], {B}} = {abc, 23, [22, 24], {22}}

ความหมายของมันจะแตกต่างออกไปอย่างสิ้นเชิงโดยที่ _int จะถูกผูกเข้ากับ 23 และหลังจากนั้นเมื่อมันเลื่อนไปเปรียบเทียบกับอิลิเมนท์ที่สองของลิสท์ที่มี ค่าเป็น 23 ซึ่งจะส่งผลให้การเปรียบเทียบล้มเหลว ปกติแล้วการใช้งานตัวแปรที่ขึ้นต้อนด้วยเครื่องหมาย _ เช่น _int นั้นทำให้โค้ดของเราอ่านได้ง่ายขึ้นแต่ผลที่ได้กลับมาคือบางครั้งเราลืมไป ว่าตัวแปรของเราถุกกำหนดค่าได้เพียงครั้งเดียวเท่านั้นและอาจส่งผลให้ โปรแกรมของเราเกิดปัญหาได้มากขึ้นด้วยเช่นกัน ดังนั้นการใช้ _ เราควรใช้งานอย่างระมัดระวัง


edit @ 15 Dec 2009 11:02:03 by cyber-climber

edit @ 15 Dec 2009 11:05:36 by cyber-climber

edit @ 16 Dec 2009 13:22:53 by cyber-climber

Erlang Term Comparison
การเปรียบเทียบเทิร์มใน Erlang นั้นจะต้องใช้นิพจน์สองตัวโดยจะต้องใส่ไว้ทั้งสองด้านของตัวดำเนินการเปรียบเทียบผลที่ได้จาการเปรียบเทียบคืออะตอมที่เป็นจริงหรือเท็จ โดยที่ตัวดำเนินการ เท่ากับ(==) และ ไม่เท่ากับ(/=) จะทำการเปรียบเทียบค่าของนิพจน์ที่อยู่ทั้งสองข้างโดยไม่สนใจประเภทของนิพจน์นั้น เช่น 1==one ผลเป็นเท็จ, one = one ผลที่ได้คือจริงต่อไป 1 == 1.0 ผลเป็นจริง, 1 /= 1.0 ผลเป็นเท็จเพราะทั้งสองแบบนี้จะมีการแปลงค่าของนิพจน์ก่อนทำการเปรียบเทียบแต่ใน Erlang ยังมีตัวกำเนินการเปรีบยเทียบอีกสองตัวที่น่าสนใจคือ เท่ากันจริง(=:=) ไม่เท่ากันจริง(=/=)  ตัวดำเนินการประเภทนี้จะทำการเปรียบเทียบทั้งค่าและประเภทไปด้วยกันดังนั้นถ้าเราทำแบบเดิมแต่เปลี่ยนตัวดำเนินการ 1 =:= 1.0 และ 1 =/= 1 ผลจะเป็นเท็จทั้งคุ่แต่ 1 =/= 1.0 จะเป็นจริงและนอกจากเราจะเปรียบเทียบการเท่ากันแล้วเรายังเปรียบเทียบเชิงลำดับได้อีกเช่น มากกว่า(>) น้อยกว่า(<) มากกว่าหรือเท่ากับ(>=) น้อยกว่าหรือเท่ากับ(<=)
สรุปเครื่องหมาย
== Equal to
/= Not equal to
=:= Exactly equal to
=/= Exactly not equal to
=< Less than or equal to
< Less than
>= Greater than or equal to
> Greater than
สำหรับการเปรียบเทียบนิพจน์ใน Erlang นั้นมีลำดับขั้นในการพิจรณาดังนี้(ในกรณีที่นิพจน์ทั้งสองเป็นประเภทที่แตกต่างกัน)

number < atom < reference < fun < port < pid < tuple < list < binary

จากหลักการเปรียบเทียบข้างบนจะเห็นได้ว่าตัวเลขจะเล็กกว่าอะตอม และทัปเปิลจะเล็กว่าลิสท์

3> 11
true
4> {123,345}<[].
true

การเปรียบเทียบลิสท์จะใช้วิธีการเปรียบเทียบไปทีละอิลิเมนท์ เปรียบเทียบไปเรื่อยๆจนกว่าด้านใดด้านหนึ่งจะหมดก็ถือว่าด้านนั้นน้อยกว่า
5> [boo,hoo]<[adder,zebra,bee].
false
6> [boo,hoo]<[boo,hoo,adder,zebra,bee].
true
ในทางกลับกันสำหรับทัปเปิลนั้นจะใช้วิธีการนับจำนวนสมาชิกของแต่ละทัปเปิลก่อนแล้วตามมาด้วยค่าของสมาชิกแต่ละตัว
7> {boo,hoo}<{adder,zebra,bee}.
true
8> {boo,hoo}<{boo,hoo,adder,zebra,bee}.
true

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

1> 1.0 == 1.
true
2> 1.0 =:= 1.
false
3> {1,2} < [1,2].
true
4> 1 =< 1.2.
true
5> 1 =/= 1.0.
true
6> (1 < 2) < 3.
false
7> (1 > 2) == false.
true

Erlang Variables
ตัวแปรถูกใช้สำหรับเก็บค่าต่างๆ สำหรับ Erlang แล้วตัวแปรนั้นจะขึ้นต้นด้วยอักษรตัวใหญ่ และอาจตามด้วยตัวอักษรตัวใหญ่หรือเล็ก ตัวเลข เครื่องหมาย _ แต่ไม่สามารถใช้อักขระพิเศษได้ยกตัวอย่างการประกาศตัวแปรกตามมาตรฐาน A_long_variable_name Flag Name2 DbgFlag แต่สำหรับตัวแปรใน Erlang นั้นจะแตกต่างจากภาษาโปรแรมมิ่งที่เราคุ้นเคยกัน โดยในสโคปของฟังก์ชั่น ที่รวมไปถึงเอิร์ลแลงเชลโพรเซส นั้นเมื่อเรากำหนดค่าให้ตัวแปรแล้วเราจะไม่สามารถเปลี่ยนค่ามันได้อีก เราเรียกกระบวนการนี้ว่าการกำหนดค่าเพียงครั้งเดียว(single assignment)
ยกตัวอย่างเช่น

Double = 2,
Double = Double * Double

ผลที่ได้คือจะเกิดเออร์เร่อขณะรันไทม์เนื่องจากเราได้กำหนดค่าให้ Double เป้น 2 ไปก่อนที่จะพยายามกำหนดให้เท่ากับ Double*Double ทางแก้คือเราต้องสร้างตัวแปรตัวใหม่ขึ้นมาเพื่อรับค่า Double*Double แทนดังนี้

Double = 2,
NewDouble = Double * Double
การกำหนดค่าครั้งเดียวนั้นอาจเป็นเรื่องแปลกสำหรับคนทั่วไปในครั้งแรกๆแต่อย่างไรก็ตาม หลังจากใช้ไปสักพักก็จะเริ่มปรับตัวเข้าหาการทำงานแบบนี้ได้ ข้อดีข้อแรกของการทำแบบนี้คือมันช่วยลดข้อผิดพลาดในการเขียนโปรแกรมลงได้เยอะมาก เนื่องจากค่าของตัวแปรไม่ถูกเปลี่ยนไปเปลี่ยนมาเรื่อยๆนั่นเอง

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

คุณสมบัติอีกข้อที่สำคัญคือเราไม่มีความจำเป็นต้องประกาศตัวแปรก่อนที่จะใช้งานมัน ดังนั้นนี่เป็นอีกแนวคิดที่ทำให้คนที่เคยเขียน C หรือ Java มาก่อนอาจะสับสนได้ สาเหตุที่ทำให้เอิร์ลแลงไม่มีเช่นนั้นเพราะเอิร์ลแลงเป็นภาษาไดนามิกส์ ประเภทของข้อมูลจะถูกกำหนดขณะทำงาน ดังนั้นจึงมีความเป็นไปได้ที่เราจะทำอะไรแปลกๆเช่นพยายามคูณตัวเลขกับตัวอักษร
 
Var = one,
Double = Var * 2
ดังนั้นข้อควรจำที่ควรคิดไว้เสมอคือตัวแปรถูกกำหนดค่าได้ครั้งเดียว ดังนั้นปัญหาอาจเกิดขึ้นในเอิร์ลแลงเชลได้เพราะโปรแกรมของเราอาจต้องทำงานอย่างต่อเนื่องไปเรื่อยๆเป็นปีๆและเราใช้เชลเพียงเชลเพียงเชลเดียวในการทำงานร่วใกบโปรแกรมนั้น ทำให้บางครั้งเราอาจทดลองอะไรบางอย่างขณะที่โปรแกรมก็ยังคงทำงานอยู่ ซึ่งอาจก่อให้เกิดปัญหาได้แต่ปะญหานี้สามารถแก้ไขได้ด้วยการใช้ f() เพื่อสั่งให้เชลลืมการกำหนดค่าของตัวแปรนั้นๆไปชั่วขณะเพื่อหลีกเลี่ยงปัญหาขณะที่เรากำลังทดลองอะไรบางอย่างอยู่เช่น

1> A = (1+2)*3.
9
2> A + A.
18
3> B = A + 1.
10
4> A = A + 1.
** exception error: no match of right hand side value 10
5> f(A).
ok
6> A.
** 1: variable 'A' is unbound **

edit @ 15 Dec 2009 11:06:08 by cyber-climber

edit @ 16 Dec 2009 18:18:04 by Rational Roof

เอิร์ลแลง(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

The Erlang Shell
ต่อไปเราจะเริ่มเปิดเชลของเอิร์ลแลงขึ้นมาเพื่อลองให้งาน โดยในยูนิกส์ให้พิมพ์คำสั่ง "erl" ลงไปมื่อเราสามารถเปิดเชลขึ้นมาแล้ว เราจะเห็นเป็น ( เลขบรรทัด> ) จากนั้นให้ลองพิมพ์ตัวเลขอะไรลงปสักอย่างเพื่อดูผลที่ได้ออกมา (อย่าลืมใส่ . เพื่อแสดงการจบคำสั่ง)
1> −234.
-234
2> 2#1010.
10
3> $A.
65

ในกรณีที่เราไม่ได้ใส่เครื่องหมาย . เข้าไปข้างหลังคำสั่งเอิร์ลแลงจะไม่รู้ว่ามันคือคำสั่งนั่นแปลว่ามันจะเก็บสิ่งที่เราพิมพ์เข้าไปเรื่อยๆจนกว่าเราจะพิมพ์ . เข้าไปนั่นเอง
4> 5-
4>
4> 4.
1
เครื่องหมาย 1>, 2> นั้นคือคอมมานด์พรอมป์ที่แสดงให้เห็นว่าเอิร์ลแลงเชลนั้นพร้อมรับคำสั่งที่เราจะป้อนเข้าไป จากพรอมป์เมื่อเราใส่ตัวเลขและจบด้วยเครื่องหมาย . และกดเอ็นเทอร์ เอิร์ลแลงะรับตำสั่งและทำการแปรออกมาเป็นเลขฐานสิบ แต่ถ้าเกิดข้อผิดพราดเราจะได้เมสเสจออกมาดังนี้
4> 5-.
* 1: syntax error before: '.'
5> q().
ในกรณีที่เราต้องการออกจากโปรแกรมก็สามารถทำได้ด้วยการพิมพ์ q().

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

#define OP_READ 1
#define OP_WRITE 2
#define OP_SEEK 3
...
#define RET_SUCCESS 223
...
ซึ่งเมื่อเรานำมันมาใช้งานในโปรแกรมหลักผลที่ได้ออกมาจะมีลักษณะนี้
int ret;
ret = file_operation(OP_READ, buff);
if( ret == RET_SUCCESS ) { ... }

ซึ่งโปรแกรมที่เขียนด้วยเอิร์ลแลงนั้นจะออกมาเป็น

Ret = file_operation(op_read, Buff),
if
Ret == ret_success ->
...

การประกาศอะตอมนั้นจะใช้ตัวอักษรตัวเล็กและตามมาด้วยชุดของตัวอักษรตามที่เราต้องการหรือจะเป็น _ หรือt @) เช่น red,ecember, cat, meters, yards, joe@somehost, และa_long_name.นอกจากนี้อะตอมยังสามรถถูกครอบด้วยเครื่องหมาย ' ได้อีกด้วยเนื่องจากในบางกรณีเราต้องการประกาศใช้งานอะตอมที่ขึ้นต้นด้วยตัวหนังสือตัวใหญ่รืออะตอมที่มีตัวอักษรพิเศษเช่น: ’Monday’, ’Tuesday’, ’+’, ’*’,
’an atom with spaces’. ดังนั้นเราจะเห็นว่าค่าของอะตอมใดๆก็คือตัวอะตอมนั่นเอง ดังนั้นเมื่อเราพิมพ์อะตอมลงไปในเชลสิ่งที่เราได้กลับมาคือค่าของอะตอมนั่นเอง
1> hello.
hello
เรื่องนี้ถือเป็นเรื่องค่อนข้างแปลกสำหรับคนที่เคยเขียนภาษาอื่นมาเมื่อเราพูดถึงเรื่องของค่าของอะตอมหรือค่าของอินทิเจอร์ แต่เนื่องจากเอิร์ลแลงเป็นภาษาเชิงฟังก์ชั่นนอล ทำให้ทุกๆคำสั่งหรือการแสดงออก(expression)จำเป็นต้องมีค่า เสมอ

Tuples
ทัปเปิลคือกลุ่มของข้อมูลของเอิร์ลแลง(ต่างชนิดกัน)ที่ถูกเก็บไว้ในรูปแบบของคอลลเล็คชั่นของไอเท็ม ทัปเปิลใช้เครื่องหมายวงเล็บปีกกาเป็นตัวกำหนด, {...}, และแต่ละอิลิเมนท์ของมันจะถูกแบ่งโดยเครื่องหมายลูกน้ำ(commas).
{123, bcd} {123, def, abc} {abc, {def, 123}, ghi} {}
{person, 'Joe', 'Armstrong'} {person, 'Mike', 'Williams'}
ทัปเปิล {123,bcd} ประกอบด้วยสองอิลิเมนท์คือ อิลิเมนท์ที่เป็นอินทิเจอร์ 123 และอิลิเมนท์ที่เป็นอะตอม bcd อีกตัวอย่างของทัปเปอร์
{abc, {def, 123}, ghi} มีสามอิลิเมนท์คือทัปเปิล {def, 123} อีกสองอิลิเมนท์เป็นอะตอมคือ abc และ ghi ส่วน {} แปลว่าทัปเปิลว่างที่ไม่มีสมาชิก ส่วนทัปเปิลที่มีเพียงอิลิเมนท์เดียวเราก็สามารถสร้างได้เช่น {123} แต่ออกจะดูแปลกที่เราจะสร้างของแบบนี้ไว้เพราะเราสามารถเก็บมันไว้บน อันทัปเปิล(untupple) ได้สำหรับทัปเปิลที่มีสมาชิกตัวแรกเป็นอะตอม เราจะเรียกสมาชิกตัวนั้นว่าแทก(TAG) โดยการใช้แทกนี้เป็นข้อตกลงพิเศษทของเอิร์ลแลงที่เราควรรู้ไว้เพราะมันถูกใช้เยอะมาก โดยมากจะใช้เพื่อบอกประเภทของข้อมูลที่เก็บในทัปเปิลเช่น
{person, 'Joe', 'Armstrong'}, อะตอม person จะบอกว่าทัปเปิลนี้ใช้เก็บข้อมูลบุคคลดังนั้นอิลิเมนท์ที่สองของทัปเปิลนี้ควรจะเป็นชื่อ ส่วนอิลิเมนท์  สองก็เป็นนามสกุล สำหรับการใช้แทกนั้นมีเพื่อใช้เพื่อระบุความแต่กต่างของทัปเปิลที่ถูกใช้สำหรับจุดประสงค์ที่ต่างกันในบริเวณต่างๆของโค้ดเรายกตัวอย่างเช่นเราสามารถหาต้นตอของปัญหาที่เกิดขึ้นได้เมื่อมีการส่งทัปเปิลผิดตัวเข้าไปหรือออกมาจากฟังก์ชั่นที่เราเรียกไป
ซึ่งการใช้แทกถือเป็นข้อปฏิบัติที่ดีในการเขียนโปรแกรมด้วยเอิร์ลแลงนอกจากนี้ทัปเปิลยังมีฟังก์ชั่นแบบบิวด์อินมาให้ใช้งานอีกด้วยเช่น

1> tuple_size({abc, {def, 123}, ghi}).
3
2> element(2,{abc, {def, 123}, ghi}).
{def,123}
3> setelement(2,{abc, {def, 123}, ghi},def).
{abc,def,ghi}
4> {1,2}<{1,3}.
true
5> {2,3}<{2,3}.
false
6> {1,2}=={2,3}.
false
คำสั่งที่สองในตัวอย่างที่เราต้องการหาอิลิเมนท์ที่สองแสดงให้เห็นว่าลำดับในทัปเปิลเริ่มด้วยเลข 1 ไม่ใช่ 0
คำสั่งที่สามเป็นการเปลี่ยนค่าอิลิเมนท์ที่สองในทัปเปิล โดยที่ผลที่ได้จะเป็นทัปเปิลใหม่ที่มีค่าตามที่เราต้องการ
โดยที่ทัปเปิลเดิมนั้นค่าก็ยังไม่มีการเปลี่ยนแปลง

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

posted on 04 Dec 2009 14:14 by roofimon  in Erlang
Erlang and Multicore
ปัจจุบันเราหลีกเลี่ยงไม่ได้ที่จะต้องใช้งานซีพียูที่มีหลายคอร์ ซึ่งสำหรับภาษา C และ Java การสร้างแอพพลิเคชั่นให้รองรับการทำงานแบบหลายคอร์นั้นเป็นเรื่องยากมากเพราะมันไม่ได้ถูกออกแบบมาให้มีคุณลักษณะแบบนี้เช่นการทำการดีบั๊กโปรแกรมที่ทำงานแบบขนานใน C และ Javaเป็นเรื่องยากมาก และแน่นอนเอิร์ลแลงทำได้ดีในเรื่องนี้มากๆ เพราะเอิร์ลแลงแตกโพรเซสออกมาโดยไม่มีการใช้เมมโมรี่ร่วมกันและสื่อสารกันผ่านการส่งเมสเสจทำให้มันเหมาะสมกับการทำงานบนซีพียูแบบหลายคอร์และทำให้มันทำงานบนซีฑียูแบบหลายคอร์ได้โดยไม่ต้องออกแบบมันใหม่ทั้งหมด

Symmetric MultiProcessing(SMP) ถูกออกแบบไว้สำหรับเอิร์ลแลงตั้งแต่ปลายช่วง 90 และปัจจุบันมันถูกบรรจุไว้ในรีลีสมาตรฐานเรียบร้อยแล้ว โดยที่ทีมพัฒนาเอิร์ลแลงโอทีพีที่อิริคสันทำการปรับแต่งและปรับปรุงให้เอสเอ็มพีทำงานได้อย่างมีประสิทธิภาพสูงสุด นั่นหมายความว่าตั้งแต่มีการรีลีสเอิร์ลแลงที่มีเอสเอ็มพีออกมา ทำให้ระบบจัดการคิวบนวีเอ็มถูกเปลี่ยนจากการทำงานบนคิวเดียว เป็นการจัดการคิวแบบหลายคิวบนหลายๆคอร์หรือซีพียูนั่นหมายความว่าการจัดการคิวจะไม่ใช่คอขวดของการประมวลผลอีกต่อไป 

หัวใจหลักของเอิร์ลแลงเอวเอ็มพีคือต้องการซ่อนปัญหาและข้อควรระวังต่างๆของเอสเอ็มพีจากโปรแกรมเมอร์เพื่อให้โปรแกรมเมอร์มีสมาธิอยู่กับการพัฒนาโค้ดอย่างที่ตัวเองต้องทำ โดยไม่ต้องมาเสียเวลากับการทำงานพื้นฐานและรวมไปถึงการทำงานร่วมกับระบบปฎิติการและฮาร์แวร์และจำนวนคอร์หรือซีพียูที่ต้องทำงานด้วย โดยที่งานเหล่านั้นจะถูกจัดการโดยเอิร์ลแลงวีเอ็มทั้งหมด

Case Studies
หลังจากเราได้เห็นความสามารถต่างๆของอิร์ลแลงไปบ้างแล้วต่อไปเรามาดูว่า มีกรณีศึกษาใดบ้างที่น่าสนใจในการนำเอิร์ลแลงไปใช้ เราควรจะเริ่มจากอิริคสันก่อนโดยเป็นโปรดักที่ชื่อ AXD301ATM Switch และตามมาด้วยโปรเจคโอเพนซอร์สอีกตัวที่น่าสนใจที่นำเอิร์ลแลงไปใช้งานนั่นคือ CouchDB 
 
The AXD301 ATM Switch
AXD301 เป็น telephony-class 10–160 Gbps เอทีเอ็มสวิทซ์ที่ถูกออกแบบและสร้างจากศูนย์ โดยที่ระยะเวลาการสร้างทั้งหมดใช้เวลาสามปี หัวใจหลักของสวิทซ์ตัวนี้คือซอฟท์แวร์ที่ถูกเขียนด้วยเอิร์ลแลงที่มีความยาวมากว่า 1.5 ล้านบรรทัดที่ทำงานร่วมกับโค้ดที่ทำหน้างานในระดับต่ำและเป็นไดร์เวอร์ที่ถูกเขียนซีและซีพลัสพลัสอีกห้าแสนบรรทัด หน้าที่หลักของเอิร์ลแลงก็คือการควบคุมลอจิกที่ซับซ้อน การจัดการและบริหารพร้อมทั้งการดูแลรักษาเอทีเอ็มสวิทซ์นี้
สวิทซ์ตัวนี้ถูกติดตั้งอย่างแพร่หลายไปทั่วโลกแต่งานที่ทำให้อุปกรณ์ชิ้นนี้มีชื่อเสียงโด่งดังมากคืองานที่บริติชเทเลคอม โดยโปรเจคนั้นรู้กันอย่างกว้างขวางว่าเป็นระบบ "Voice over ATM" ที่ใหญ่ที่สุดในโลกและผลการทำงานหลังจากการทำงานวันแรกของโหนดนี่ในปี 2002 พบว่าระบบมีความน่าเชื่อถือสูงมากระดับ 99.999999% 

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

จากความสำเร็จของโปรเจคนี้ทำให้อิริกสันนำเอิร์ลแลงไปใช้ในโปรเจคอื่นๆอีกหลายโปรเจคไม่ว่าจะเป็น SIP telephony stack, ซอฟท์แวร์ที่ใช้ในการควบคุมสถานีแบบไร้สาย, telephony gateway,media gateways, broadband solutions, and in GPRS and 3G data transmission.

CouchDB
เมื่อนายเดเมี่ยน แคทซ์ตัดสินใจที่จะสร้างเคาช์ดีบีเค้าต้องการที่จะพัฒนาอะไรที่มัน "แจ๋ว" เค้าเองต้องการที่จะพิสูจน์ตัวเองว่าตัวเองมีความสามารถมากพอที่จะพัฒนาอะไรสักอย่างจากศูนย์หรือไม่สิ่งที่เค้าต้องการทำคือฐานข้อมูลที่ถูกสร้างบนพื้นฐานของ schema-less replicated document store, และเก็บออบเจคในรูปแบบของ JSON, ข้อมูลเหล่านั้นจะถูกใช้งานป่าน RESTful 

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

เอิร์ลแลงมีคุณสมบัติครบตามที่เดเมียนต้องการเพื่อสร้างฟังก์ชั่นการทำงานพิเศษให้เคาช์ดีบีทุกข้อ เขาจึงเริ่มย้ายโค้ดเปลี่ยนไปเป็นเอิร์ลเลงสำหรับส่วนของการทำงานแบบพร้อมกันหลายโพรเซสแทนที่โค้ดเดิมที่เป็นซีพลัสพลัส แต่สุดท้ายแล้วโค้ดทั้งหมดของเคาช์ดีบีถูกเขียนใหม่ทั้งหมดเป็นเอิร์ลแลง!!!! เนื่องจากเอิร์ลแลงมีสิ่งที่เขาต้องการในการสร้างระบบฐานข้อมูลเช่น ระบบจัดการไอโอที่แข็งแรงมาก มีความน่าเชื่อถือสูง กระบวนการจัดการกับเออร์เร่อร์ที่ดี และหลังจากทำการทดสอบเปรียบเทียบสมรรถนะแอพพลิเคชั่นตัวใหม่(ยังไม่ได้ปรับแต่งโค้ด) พบว่ามันสามารถรับโหลดได้ประมาณ
20,000 คอนเน็กชั่นพร้อมกัน ซึ่งเมื่อเทียบกับแอพพลิเคชั่นตัวเดิมที่เป็นซีพลัสพลัสซึ่งรับได้เพียง 500 คอนเน็กชั่นเท่านั้น !!! ตอนนี้เคาช์บีดีถือเป็นโปรเจคที่มีชื่อเสียงมากที่ถูกเขียนด้วยเอิร์ลแลงและโค้ดของโปรเจคนนี้เป็นแหล่ง
ที่ใช้ศึกษาเกี่ยวกับเอิร์ลแลงที่ดีมาก

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

posted on 03 Dec 2009 09:46 by roofimon  in Erlang
High-Level Constructs
เอิร์ลแลงเป็นภาษาเชิงบอกเล่า(declarative) โดยที่ลักษณะเด่นของภาษาแบบนี้คือตัวมันเองจะอธิบายว่าอะไรควรจะถูกคำนวณ ไม่ใช่ว่าค่าที่ได้มานี้ถูกคำนวณมาอย่างไร(โคตรงง) ในเอิร์ลแลงการประกาศฟังก์ชั่นจะใช้กระบวนการเปรียบเทียบแพทเทิร์น(Pattern Matching) เพื่อเลือกว่าฟังก์ชั่นใดควรทำงาน นอกจากนี้เรายังใช้กระบวนการนี้เข้ามาช่วยในการดึงเอาข้อมูลอะไรบางอย่างออกมาจากโครงสร้างข้อมูลที่ซับซ้อน ดังนั้นการทำการเปรียบเทียบแพทเทิร์นนั้นจะมองแล้วเหมือนชุดของสมการทางคณิตศาสตร์เช่น

area({square, Side}) -> Side * Side ;
area({circle, Radius}) -> math:pi() * Radius * Radius.

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

decode(<< SourcePort:16, DestinationPort:16,
SequenceNumber:32,
AckNumber:32,
DataOffset:4, _Reserved:4, Flags:8, WindowSize:16,
Checksum:16, UrgentPointer:16,
Payload/binary>>) when DataOffset>4 ...

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

qsort([]) -> [];
qsort([X|Xs]) ->
qsort([Y || Y<-Xs, Y =< X]) ++ [X] ++ qsort([Y || Y<-Xs, Y > X]).

Concurrent Processes and Message Passing
การทำงานของโพรเซสพร้อมๆกันในเอิร์ลแลงเป็นความสามารถพื้นฐานของมันอยู่แล้วและสำหรับเอิร์ลแลงแล้ว
มันจะทำการสร้างโพรเซสที่มีรีซอร์สเป็นของตัวเองออกมาทุกครั้งแทนที่จะสร้างเทรดที่ใช้รีซอร์สร่วมกันเนื่องจากมันต้องการป้องกันปัญหาในเรื่องของการรบกวนกันของเทรด ซึ่งอาจจะนำมาซึ่งปัญหาเรื่องของเดดล๊อคได้

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

Scalable, Safe, and Efficient Concurrency
การทำงานแบบพร้อมกันของโพรเซสในเอิร์ลแลงนั้น เร็ว และขยายออกไปได้ง่ายมากเนื่องจาก โพรเซสต่างๆที่เกิดขึ้นจะถูก สร้าง จัดตาราง และจัดการทำงานอยู่ภายใต้ เอิร์ลแลงวีเอ็มซึ่งจะเป็นอิสระจากระบบปฏิบัติการที่อยู่ในระดับต่ำลงไปกว่า ทำให้ช่วงเวลาที่ใช้ในการสร้างโพรเซสอยู่ในระดับไมโครวินาที และไม่ขึ้นกับจำนวนโพรเซสที่ถูกสร้างไว้ก่อนหน้านี้แล้ว ซึ่งเมื่อเราเปรียบเทียบสิ่งนี้กับ Java หรือ C# แล้วจะเห็นว่าทั้งสองภาษานั้นจะสร้างโพรเซสภายใต้ระบบปฎิบัตการโดยตรงทำให้เราต้องไปแข่งขันกับระบบอื่นอย่างยากลำบากเพื่อขอใช้รีซอร์ส นอกจากนี้เอิร์ลแลงยังสื่อสารกันระหว่างโพรเซสด้วยการส่งเมสเสจและกระบวนการส่งก็เป็นเพียงแค่การคัดลอกข้อมูลจากเมมโมรี่สเปซหนึ่งไปยังอีกที่หนึ่งเท่านั้น
ทำให้กระบวนการส่งเมสเสจเป็นไปได้อย่างรวดเร็ว 

Soft Real-Time Properties
ถึงแม้ว่าเอิร์ลแลงจะเป็นภาษาระดับสูงแต่มันเองก็สามารถถูกใช้สำหรับงานประเภทซอฟท์เรียลไทม์ได้เนื่องจากหลายปัจจัยเช่นการจัดการการใช้รีซอร์สทำผ่านกระบวนการการ์เบจคอลเลคชั่นในด้วยโพรเซสเองเลยทำให้มันทำงานได้เร็วมากนั่นแปลว่ามันทำการรับโหลดได้สูงมากโดยที่ผลตอบสนองไม่ตกแม้กระทั่งในช่วงเวลาที่โหลดสูงกมาก็ตาม

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

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

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

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

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

Distributed Computation
อย่างท่เรารู้กันแล้วว่าการประมวลผลแบบกระจายเป็นความสามารถพื้นฐานของเอิร์ลแลง โดยที่ฟังก์ชั่นการทำงานนี้เอิร์ลแลงโดยปกติจะทำงานบน ทีซีพี/ไอพี ดังนั้นทุกโหนดบนระบบเน็ทเวิร์คใดๆก็ตามสามารถต่อเข้าหากันได้ ตราบเท่าที่โหนด
เหล่านั้นทำงานบน ทีซีพี/ไอพี และไฟร์วอลถูกกำหนดค่าอย่างถูกต้อง

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

Integration and Openness
เนื่องจากในชีวิตจริง เราต้องทำงานกับแพลทฟอร์มและภาษาต่างๆที่หลากหลายมาก ตัวเอิร์ลแลงเองก็สามารถทำงานกับภาษาต่างๆหลายภาษาได้เช่น C, Java,Ruby, Python, Perl, LIPS และอื่นๆด้วยไลบรารี่ระดับสูงในเอิร์ลแลง ทำให้เราสามารถมองโหนดอื่นๆที่มีแอพพลิเคชั่นที่ถูกเขียนด้วยภาษาอื่นเช่น Java, C ให้เป็นเอิร์ลแลงโหนดได้และนอกจากนีเราสามารถเชื่มภาษาเหล่านั้นเข้ากับเอิร์แลงได้อย่างแนบแน่นด้วยการใช้ไดร์เวอร์เพื่อเชื่อมเข้ากับเอิร์แลงวีเอ็ม ทำให้เรามองมันเป็นเหมือนอุปกรณ์ชนิดหนึ่งที่เราสามารถเปิดซ๊อกเก็ทเพื่อสื่อสารกันได้ โดยการสื่อสารสามารถทำป่านโปรโตคอลยอดนิยมอย่าง HTTP, SNMP และ IIOP

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

edit @ 3 Dec 2009 15:01:02 by cyber-climber

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

posted on 02 Dec 2009 15:11 by roofimon  in Erlang

ไม่ได้เขียน blog ที่นี่นานมากอยากเขียนเรื่องเอิร์ลแลงเก็บไว้อ่านเองไม่รู้จะไปเขียนที่ไหนก็เลยนึกถึงที่นี่ก่อน แต่!!!ไม่ได้เข้ามานาน แน่นอนลืมรหัสผ่านพยายามมั่วอยู่หนึ่งคืนก็ทนไม่ได้ ต้องกดสูตรลับ ผมก็ได้รหัสใหม่มาภายในสิบนาที

 ทำไมต้องเอิร์ลแลง(Erlang)ภาษาอื่นก็มีตั้งมากมายล้านแปดที่จะให้ไปอ่านเล่นไม่ว่าจะเป็น สกาล่า(Scala), รูบี้(Ruby), ไพธอน(Python) และอื่นๆอีกมากมายแต่สำหรับผมแล้วมีเหตุผลอยู่สองข้อ

 

  • ภาษานั้นต้องไม่ทำงานบนเจวีเอ็ม เพราะผมเบื่อมันมากกกกกก
  • ภาษานั้นต้องไปเป็นการเขียนโปรแกรมแบบเชิงวัตถุ(Object Oriented) เพราะอยากเปลี่ยนวิธีคิดบ้าง 

 

ดังนั้นหวยเลยออกที่เอิร์ลแลง ก่อนอื่นเรามาทำความรู้จักกับภาษาก่อนว่ามันคืออะไร

1.1 เอิร์ลแลงในเปลือกนัท

เอิร์แลงเองเป็นทั้งแพลทฟอร์มและเป็นภาษาสำหรับการใช้งานทั่วไป โดยที่ตัวมันเองถูกออกแบบมาให้สามารถรองรับการทำงานหลายๆงานพร้อมกัน(Concurrent), กระจายการทำงาน(Distribution) และทนต่อความผิดพลาด(Fault Tolerance)ดังนั้นเราจะเห็นการนำเอาภาษานี้ไปใช้งานในโดเมนของระบบการสื่อสารเยอะมากเช่นบริษัทอิริกสัน(Ericsson)เป็นบริษัทที่ใช้งานภาษาเอิร์ลแลงสำหรับการสร้างระบบสื่อสารขนาดใหญ่ของตัวเองเยอะมาก
 

1.2 อะไรคือโอทีพี(OTP)

โอทีพีเป็นคำย่อของมาตรฐานเทเลคอมแบบเปิด(Open Telecom Platform)  ตัวมันเองเป็นไลบารี่ขนาดใหญ่ที่ช่วยให้เราสามาถทำได้ตั้งแต่การคอมไพล์ Abstract Syntax Notation One (ASN.1) ไปจนถึงการเตรียมเวบเซิร์ฟเวอร์ ดังนั้นทุกโปรเจคที่ใช้เอิร์ลแลงส่วนใหญ่จะใช้ เอิร์ลแลง/โอทีพี ไปโดยธรรมชาติแล้วนั่นเอง

1.3 แอพพลิเคชั่นประเภทไหนที่ควรจะใช้เอิร์ลแลง

 

  • การประมวลผลแบบกระจาย, งานที่ต้องการความน่าเชื่อถือสูงมาก และการทำงานแบบซอฟท์เรียวไทม์
  • ระบบทางโทรคมเช่นการควบคุมสวิช, การแปลงโปรโตคอล
  • เซิร์ฟเวอร์สำหรับอินเทอร์เน็ทแอพพลิเคชั่น ยกตัวอย่างเช่น เอเจนท์สำหรับส่งเมล์ เซิร์ฟเวอร์ IMAP-4,HTTP เซิร์ฟเวอร์หรือ WAP สแต็ค.
  • แอพพลิเคชั่นของระบบโทรคม เช่น การควบคุมระบบเน็ทเวิร์คสำหรับโทรศัพท์เคลื่อที่ 
  • แอพพลิเคชั่นทั่วไปที่ต้องการความสามารถระดับซอฟท์เรียวไทม์

 

เอิร์ลแลงเป็นแพลทฟอร์มที่เหมาะสมมากกับงานเหล่านี้เพราะปัญหาเหล่านี้ถือเป็นอินพุทสำหรับการออกแบบตัวเอิร์ลแลงเองตั้งแต่แรกเริ่ม ทำให้มันมีความสามารถต่างๆที่พร้อมจะถูกใช้ในการแก้ปัญหาเหล่านี้ได้เช่น

 

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

 

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

 

1.4 ใครใช้งานเอิร์ลแลงอยู่บ้าง

คนที่ใช้งานเอิร์ลแลงมากที่สุดก็คืออิริกสัน โดยระบบที่ใหญ่ที่สุดของอิริคสันที่ใช้เอิร์ลแลงคือ AXD301 ATM Switch โดยปริมาณที่โค้ดของเอิร์ลแลงที่ใช้สำหรับโปรเจคนี้คือ 850 kloc และ 1Mloc ของ C/C++

บริษัทอื่นๆที่ใช้งานเอิร์ลแลง

 

  • Bluetail/Alteon/Nortel (distributed, fault tolerant email system, SSL accelerator) 
  • Cellpoint (Location-based Mobile Services) 
  • Corelatus (SS7 monitoring). 
  • dqdp.net (in Latvian) (Web Services). 
  • Facebook (Facebook chat backend) 
  • Finnish Meteorological Institute (Data acquisition and real-time monitoring) 
  • IDT corp. (Real-time least-cost routing expert systems) 
  • Kreditor (Electronic payment systems) 
  • Mobilearts (GSM and UMTS services) 
  • Netkit Solutions (Network Equipment Monitoring and Operations Support Systems) 
  • Process-one (Jabber Messaging) 
  • Schlund + Partner (Messaging and Interactive Voice Response services) 
  • Quviq (Software Test Tool) 
  • RabbitMQ (AMQP Enterprise Messaging) 
  • T-Mobile (previously one2one) (advanced call control services) 
  • Telia (a telecomms operator) 
  • Vail Systems (Interactive Voice Response systems) 
  • Wavenet (SS7 and IVR applications) 

 

 

 

 

 

 

@chanwit ได้รับแรงบันดาลใจจากรายงานการเลือกตั้งประธานาธิปดี ประเทศเจ้าโลกครับ โดยมีคนเก่งเขียนแอพพลิเคชั่นดึงเอาข้อความต่างๆ ที่มีคนพูดถึงผู้สมัคร

@chanwit เลยเขียนแอพพลิเคชั่นมาอีกหนึ่งตัวที่ดึงเอาข้อความใน twiiter ทีทีคนพูดถึงผู้สมัคร 5 คนขึ้นมให้เราอ่านประกอบไปด้วย ประภัสร์ อภิรักษ์ ลีน่า ชูวิทย์ และ เกรียงศักดิ์ ใครสนใจติดตามข่าวสารแบบรวดเร็วทันใจสามารถเข้าไปลองเล่นได้ที่

เลือกตั้งผู้ว่ากรุงเทพ 51<< click ที่นี่ครับ

 

edit @ 30 Sep 2008 22:42:24 by cyber-climber