การเขียนฟังก์ชันใน MATLAB

หากใครได้อ่านตั้งแต่ MATLAB พื้นฐาน 1 และพื้นฐาน 2 จนถึงหัวข้อปัจจุบัน จะสังเกตุเห็นว่าการเขียนโค้ดทั้งหมดนั้น จะเขียนบน script (การเขียนโปรแกรมใน m-file ทั่วๆ ไปเรียกว่าการเขียนแบบ script) หรือเขียนบน command window เท่านั้น นั่นก็เพราะว่าการเขียนด้วยวิธีนี้เหมาะกับมือใหม่ที่เพิ่งหัดเขียน เนื่องจากสามารถดูข้อมูลตัวแปรต่างๆ ได้ง่าย ทำให้เราสามารถตรวจเช็คโปรแกรมได้ ถึงแม้โปรแกรมจะ error และหยุดทำงานไปแล้ว (ข้อมูลของตัวแปรต่างๆ จะถูกเก็บไว้ใน workspace)

แต่การเขียนโปรแกรมแบบ script ก็มีข้อจำกัดของมัน นั่นก็คือ มันไม่สามารถส่งค่าจากโปรแกรมหนึ่งไปอีกโปรแกรมหนึ่งได้ และมันก็ไม่สามารถรับค่าจากโปรแกรมอื่นได้เช่นกัน

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

1. ผู้ใช้งานต้องทราบลำดับการรัน ถึงจะใช้งานโปรแกรมได้

2. มันไม่สะดวกในการใช้งาน เพราะผู้ใช้งานต้องรอ เพื่อกดปุ่ม RUN ทีละไฟล์ ซึ่งถ้าหากไฟล์นั้นใช้เวลานาน ผู้ใช้งานก็ต้องนั่งรออยู่หน้าคอมฯ ทำให้เสียเวลาทำงานอย่างอื่น

3. เมื่อโปรแกรมไม่ได้ทำงานอย่างต่อเนื่อง ก็มีโอกาสเกิดข้อผิดพลาดขึ้นได้ เช่น หลังจากรัน script ที่ 1 เสร็จ ปกติต้องรัน script ที่ 2 ต่อ แต่ผู้ใช้งานอาจจะทดลองเปลี่ยนค่าตัวแปรสักตัวเพื่อดูผลลัพธ์ที่ต่างออกไป แล้วค่อยไปกดรัน script ที่ 2 โดยที่ลืมไปว่าตัวเองได้เปลี่ยนค่าตัวแปรในโปรแกรม ซึ่งจะทำให้ผลลัพธ์ที่ได้นั้นผิดพลาดโดยที่ผู้ใช้งานไม่รู้ตัว

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

5. เมื่อเรามาอ่านโปรแกรมในภายหลัง เราจะไม่เข้าใจ เพราะปกติแล้ว ก่อนที่เราจะเรียกใช้ตัวแปรใดๆ ก็ตาม เราต้องประกาศ หรือกำหนดค่าให้ตัวแปรนั้นก่อน แต่การเขียนโปรแกรมแบบ script หลายไฟล์ กลับใช้วิธีอ่านข้อมูลตัวแปรจาก workspace ดังนั้นเมื่อเรามาอ่านโปรแกรมในภายหลัง เราก็จะเห็นตัวแปรหลายๆ ตัวถูกเรียกใช้งานเลย โดยที่ยังไม่เคยถูกประกาศ หรือกำหนดค่ามาก่อนในโปรแกรมนั้น ทำให้เราไม่รู้ว่าตัวแปรนั้นเก็บข้อมูลอะไร และมันมากจากไหน

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

โปรแกรมแบบ "สคริปต์" และ "ฟังก์ชัน" ต่างกันอย่างไร? 

สคริปต์ ฟังก์ชัน
1.ชื่อไฟล์ อะไรก็ได้ ต้องชื่อเดียวกับฟังก์ชัน
2.รับค่า/ส่งค่า ไม่ได้ ได้
3.เก็บตัวแปรไว้ใน workspace ใช่ ไม่ช่
4.เรียกดูตัวแปรหลังจากรันเสร็จแล้ว ได้ ไม่ได้
5.เขียนหลายโปรแกรมไว้ในไฟล์เดียวกัน ไม่ได้ ได้
6.รันด้วยตัวเองได้ (กดรันได้) ได้ ไม่ได้*

*ฟังก์ชันโดยทั่วไป ไม่สามารถรันด้วยตัวเองได้ (จะถูกเรียกใช้งานจากโปรแกรมอื่นเท่านั้น) แต่ก็มีฟังก์ชันบางประเภทที่รันด้วยตัวเองได้ (อ่านต่อไปเดี๋ยวก็รู้เอง)

การสร้างฟังก์ชันทำอย่างไร?

การสร้างฟังก์ชันนั้นง่ายมากครับ เพียงแค่ต้องเขียนคำว่า "function" ไว้บรรทัดบนสุด และเขียนคำว่า "end" ไว้บรรทัดล่างสุด แค่นี้ก็ไฟล์ประเภทฟังก์ชันแล้วครบ

*หมายเหตุ คำว่า "end" ที่บรรทัดล่างสุดจะใส่หรือไม่ใส่ก็ได้ แต่ผมแนะนำให้ใส่ไว้เลยครับ เพราะการเขียนฟังก์ชันบางประเภท ก็จำเป็นต้องใส่ end เมื่อจบฟังก์ชัน แต่หากคุณเขียนฟังก์ชันในโปรแกรม GUI ห้ามใส่ end หลัง function นะครับ

ซึ่งโครงสร้างโดยทั่วไปของฟังก์ชัน จะเป็นดังนี้
function [output1,... ,outputN] = functionName(input1,... ,inputN)
...[code]...
end

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

กฎสำคัญในการสร้างฟังก์ชัน

1. ชื่อไฟล์กับชื่อฟังก์ชัน ต้องเป็นชื่อเดียวกันเสมอ (ยกเว้น private function)

2. ชื่อไฟล์ห้ามขึ้นต้นด้วยตัวเลข หรืออักขระที่ไม่ใช่ตัวอักษรภาษาอังกฤษ

3. ห้ามตั้งชื่อซ้ำกับฟังก์ชันของ MATLAB เช่น ห้ามตั้งชื่อว่า "importdata" , "imread" , "imwrite" เพราะชื่อเหล่านี้เป็นชื่อฟังก์ชันของ MATLAB หากเราไม่มั่นใจว่าชื่อที่เราตั้งจะซ้ำหรือไม่ ให้ตั้งชื่อด้วยตัวอักษรใหญ่ทั้งหมด เพราะฟังก์ชันของ MATLAB ส่วนมากจะเป็นตัวอักษรเล็ก หรือตัวเล็กผสมตัวใหญ่

4. ห้ามเขียนโปรแกรมแบบ script และ function ไว้ในไฟล์เดียว (เพราะโปรแกรมจะ error)

ฟังก์ชันมีกี่ประเภท อะไรบ้าง?

ประเภทที่ 1 ฟังก์ชันที่ไม่ต้องการ input และไม่มี output

*ชื่อไฟล์ fcnex1.m
function fcnex1
clc;
disp('Hello');
STR = datestr(now,'HH:MM:SS');
fprintf('Time: %s\n',STR);
end

สังเกตุดูนะครับว่าหลัง "function" จะไม่มี output และ input จะมีแต่ชื่อฟังก์ชันเท่านั้น ดังนั้นฟังก์ชันประเภทนี้จึงรันด้วยตัวเองได้ (ฟังก์ชันที่ไม่มี input สามารถรันด้วยตัวเองได้ทั้งหมด) หากใครอยากดูผลรันของฟังก์ชันนี้ copy ไปใส่ไว้ใน m-file แล้วตั้งชื่อว่า fcnex1.m แล้วรันบน MATLAB ดูได้เลยครับ

ประเภทที่ 2 ฟังก์ชันที่ไม่มี input แต่มี output

*ชื่อไฟล์ fcnex2.m
function op1 = fcnex2
op1 = datestr(now,'HH:MM:SS');
end

ผู้อ่านจำไว้ให้ดีนะครับว่า ตัวแปรที่อยู่หลัง "function" และอยู่หน้าเครื่องหมายเท่ากับ นั่นคือตัวแปร output ซึ่งจะชื่อว่าอะไรก็ได้ และมีกี่ตัวก็ได้ หากมีตัวเดียว เราสามารถเขียนไว้โดดๆ เหมือนในตัวอย่างนี้ได้เลย แต่หากมี output หลายตัว จะต้องใส่เครื่องหมาย [ - ] และใส่ comma คั่นระหว่าง output แต่ละตัวดังนี้
function [tm1,tm2] = fcnex2
tm1 = datestr(now,'HH:MM:SS');
pause(1)
tm2 = datestr(now,'HH:MM:SS');
end

สังเกตุดูนะครับว่า การเขียนฟังก์ชันใน MATLAB จะไม่ใช้คำสั่ง return เพื่อคืนค่า เพียงแค่เรากำหนดชื่อตัวแปรนั้นให้เป็น output เมื่อฟังก์ชันคำนวณเสร็จ ฟังก์ชันก็จะส่งค่าตัวแปรที่อยู่ในรายชื่อ output ทั้งหมด กลับไปที่โปรแกรมต้นทางที่เรียกใช้งานฟังก์ชันโดยอัตโนมัติ แต่อย่าลืมนะครับว่าหากฟังก์ชันมี output 2 ตัว ฝั่งที่เรียกฟังก์ชัน ก็ต้องมีตัวแปรมารับค่า 2 ตัวด้วยเช่นกัน ตัวอย่างเช่น

>> [t1 , t2] = fcnex2

t1 =

    '03:59:21'


t2 =

    '03:59:22'

ซึ่งฟังก์ชันจะส่งค่ากลับคืนตามตำแหน่งของตัวแปร เช่นในที่นี่ output tm1 ก็จะถูกเก็บไว้ในตัวแปร t1 และ output tm2 ก็จะถูกเก็บไว้ในตัวแปร t2 และถ้าหากเรามีตัวแปรมารับค่าไม่พอ ค่า output ตัวที่เหลือก็จะไม่ถูกส่งกลับมา ตัวอย่างเช่น

>> tm = fcnex2

tm =

    '03:59:53'

ตัวแปร output tm1 ก็จะถูกส่งมาให้ตัวแปร tm แต่ตัวแปร output tm2 จะไม่ถูกส่งกลับมา แต่ถ้าหากเรากำหนดตัวแปรรับค่าเกิน โปรแกรมจะแจ้งเตือน error ดังนี้

>> [t1,t2,t3] = fcnex2
Error using fcnex2
Too many output arguments.

เนื่องจากว่าฟังก์ชัน fcnex2 มี output แค่ 2 ตัว แต่เรากำหนดตัวแปรรับค่าถึง 3 ตัว ได้แก่ t1, t2 และ t3 โปรแกรมก็เลย error เพราะฟังก์ชันไม่สามารถหาค่า output ตัวที่ 3 ให้เราได้

ประเภทที่ 3 ฟังก์ชันที่มี input แต่ไม่มี output

*ชื่อไฟล์ fcnex3.m
function fcnex3(cnt)
%Count down function
for m=cnt:-1:0
    clc;
    A = 124.*ones(1,m);
    B = [char(A) num2str(m)];
    disp(B);
    pause(1);
end
end

ผลรัน

ฟังก์ชันประเภทนี้ส่วนมากจะเป็นฟังก์ชันที่เอาไว้แสดงผล หรือสร้าง UI เป็นหลัก และการรันฟังก์ชันชนิดนี้จะต้องเรียกใช้งานผ่าน command window หรือเรียกใช้งานผ่านโปรแกรม หรือ ฟังก์ชันอื่นๆ เท่านั้น ไม่สามารถรันด้วยตัวเองได้ (เพราะฟังก์ชันต้องการ input)

ประเภทที่ 4 ฟังก์ชันที่ต้องการ input และมีค่า output

*ชื่อไฟล์ fcnex4.m
function C = fcnex4(A,B)
%Triangle
C = (A^2)+(B^2);
C = sqrt(C);
end

ตัวอย่างผลรัน

>> C = fcnex4(12,8.5)

C =

   14.7054

ฟังก์ชันประเภทนี้ไม่สามารถรันด้วยตัวเองได้ (เหมือนประเภทที่ 3)
สังเกตุดูนะครับว่า input แต่ละตัวจะถูกคั่นด้วยเครื่องหมาย คอมม่า ( , ) ซึ่งจำนวน input ที่เราใส่ให้ฟังก์ชันจะต้องมีจำนวนเท่ากับที่เขียนไว้ในฟังก์ชันเสมอ เช่นในตัวอย่างนี้ ฟังก์ชัน fcnex4 มี input 2 ตัวคือ A และ B เมื่อเรียกใช้งาน ก็ต้องป้อน input ให้ฟังก์ชัน fcnex4 จำนวน 2 ตัวเสมอ ไม่มาก หรือ น้อยไปกว่านั้น ไม่ใช่นั้นโปรแกรมจะ error

ประเภทที่ 5 ฟังก์ชันที่ไม่กำหนดจำนวน input และ output

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

*ชื่อไฟล์ fcnex5.m

function varargout = fcnex5(varargin)
% Calculate triangle or square
if(nargin<3 || nargin>4)
    Area1 = 0;   %square meters
    Area2 = 0;   %Rai (Thai)
end
if(nargin==3)
    A = varargin{1};
    B = varargin{2};
    C = varargin{3};
    S = (A+B+C)/2;
    Area1 = sqrt(S*(S-A)*(S-B)*(S-C));
    Area2 = Area1/1600;
end
if(nargin==4)
    A = varargin{1};
    B = varargin{2};
    C = varargin{3};
    D = varargin{4};
    S = (A+B+C+D)/2;
    Area1 = sqrt((S-A)*(S-B)*(S-C)*(S-D));
    Area2 = Area1/1600;
end

if(nargout==0)
    %no output
end
if(nargout==1)
    varargout{1} = Area1;
end
if(nargout>=2)
    varargout{1} = Area1;
    varargout{2} = Area2;
    for m=3:nargout
        varargout{m} = 0;
    end
end
end

คำสั่งพิเศษในฟังก์ชัน fcnex5

- varargin ย่อมาจาก Variable Argument Input
ใช้คำสั่งนี้เป็น input ให้ฟังก์ชัน จะทำให้ฟังก์ชันรับค่า input กี่ตัวก็ได้

- varargout ย่อมาจาก Variable Argument Output
ใช้คำสั่งนี้เป็น output ให้ฟังก์ชัน จะทำให้ฟังก์ชันส่งค่า output ออกไปกี่ตัวก็ได้

- nargin ย่อมาจาก Number of Argument Input
ใช้นับจำนวน input ของคำสั่ง varargin

- nargout ย่อมาจาก Number of Argument Output
ใช้นับจำนวน output ของคำสั่ง varargout (จำนวน output ของฟังก์ชันจะนับตอนที่ผู้ใช้งานเรียกใช้ อย่างเช่น ถ้าเอาตัวแปรมารับค่า 2 ตัว ก็คือมีจำนวน output 2 ตัว)

คำอธิบายการทำงานของฟังก์ชัน fcnex5

ฟังก์ชัน fcnex5 เป็นฟังก์ชันที่ใช้คำนวณพื้นที่ของรูป 3 เหลี่ยม และรูป 4 เหลี่ยม ทุกประเภท ไม่ว่าจะเป็นสามเหลี่ยมด้านเท่า สามเหลี่ยมมุมฉาก สามเหลี่ยมด้านไม่เท่า สี่เหลี่ยมจัตุรัส สี่เหลี่ยมผืนผ้า สี่เหลี่ยมด้านไม่เท่า

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

ถ้าหากผู้ใช้งานไม่มีตัวแปรรับค่า output เลย ฟังก์ชันนี้ก็จะไม่คืนค่าอะไรออกมา แต่ถ้ามีตัวแปรรับ output ตัวแปรเดียว ฟังก์ชันนี้ก็จะคืนค่า พื้นที่ในหน่วยตารางเมตร อย่างเดียว ถ้ามีตัวแปรรับค่า 2 ตัว ก็จะคืนค่าพื้นที่ทั้งในหน่วยตารางเมตร และพื้นที่ในหน่วยไร่ แต่ถ้าหากมีตัวแปรรับค่า output มากกว่า 2 ตัว ตัวแปรตั้งแต่ตัวที่ 3 เป็นต้นไป จะมีค่าเป็น 0

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

ตัวอย่างผลรัน


1. ไม่มีตัวแปรรับค่า output และไม่ป้อน input ให้ฟังก์ชัน ผลที่ได้คือ ไม่มีค่าอะไรคืนกลับมา

>> fcnex5
>>

2. ป้อน input น้อยกว่า 3 ตัว หรือมากกว่า 4 ตัว ผลลัพธ์ที่ได้จะเป็น 0

>> [A,B] = fcnex5(10,10)

A =

     0

B =

     0

>> [A,B] = fcnex5(10,10,10,10,5)

A =

     0

B =

     0

3. มีตัวแปรรับค่าแค่ตัวเดียว จะได้คำตอบเป็นพื้นที่ในหน่วยตารางเมตรอย่างเดียว

A = fcnex5(10,10,10)

A =

   43.3013

พื้นที่ 3 เหลี่ยมด้านเท่าขนาด 10x10x10 เมตร มีพื้นที่เท่ากับ 43.3013 ตารางเมตร

4. มีตัวแปรรับค่า 2 ตัว จะได้คำตอบในหน่วยตารางเมตร และ ไร่

>> [A,B] = fcnex5(10,10,10,10)

A =

   100


B =

    0.0625

พื้นที่ 4 เหลี่ยมด้านเท่าขนาด 10x10x10x10 เมตร มีพื้นที่เท่ากับ 100 ตารางเมตร หรือ 0.0625 ไร่

5. มีตัวแปรรับค่ามากกว่า 2 ตัว ตั้งแต่ตัวที่ 3 ไปค่าจะเป็น 0 ทั้งหมด

>> [A,B,C,D,E] = fcnex5(10,8,4,14)

A =

   66.9328


B =

    0.0418


C =

     0


D =

     0


E =

     0

พื้นที่ 4 เหลี่ยมด้านไม่เท่าขนาด 10x8x4x14 เมตร มีพื้นที่เท่ากับ 66.9328 ตารางเมตร หรือ 0.0418 ไร่


คำอธิบายเพิ่มเติมเพื่อความเข้าใจ

หากเราเขียนเหมือนในตัวอย่างฟังก์ชัน fcnex5 เราไม่จำเป็นต้องป้อน input หรือ output เลยสักตัวก็ได้ แต่ถ้าหากเราอยากกำหนดจำนวน input อย่างน้อย หรือ output อย่างน้อย เราสามารถใช้คำสั่ง varargin หรือ varargout ร่วมกับตัวแปรธรรมดาได้ เช่น

function [Area1, varargout] = fcnex5(A, B, C, varargin)

หากเราเขียนอย่างเช่นข้างบนนี้ หมายความว่า ฟังก์ชันนี้ต้องการ input อย่างน้อย 3 ตัว ถ้าใส่น้อยกว่านี้ฟังก์ชันจะ error แต่ใส่มากกว่า 3 ตัวได้ ไม่มีปัญหาอะไร และ output ต้องมีอย่างน้อย 1 ตัว หากไม่มีตัวแปรมารับค่า MATLAB จะเก็บคำตอบไว้ในตัวแปรที่ชื่อว่า ans โดยอัตโนมัติ

และคำสั่ง nargin และ nargout จะนับจำนวน input และ output ทั้งหมด ไม่ใช่แค่นับจากคำสั่ง varargin และ varargout อย่างเดียว ตัวอย่างเช่น

[a,b,c] = fcnex5(10,8,2,4,7)

การเทียบค่า input จะเป็นดังนี้
10 ถูกเก็บไว้ใน A
8   ถูกเก็บไว้ใน B
2   ถูกเก็บไว้ใน C
4   ถูกเก็บไว้ใน varargin{1}
7   ถูกเก็บไว้ใน varargin{2}
คำสั่ง nargin มีค่าเท่ากับ 5

การเปรียบเทียบค่า ouput
a เก็บค่า Area
b เก็บค่า varargout{1}
c เก็บค่า varargout{2}
คำสั่ง nargout มีค่าเท่ากับ 3

ประเภทที่ 6 Anonymous function

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

ข้อกำหนดการเขียน Anonymous function
1. จะต้องมีการคำนวณแค่ 1 บรรทัดเท่านั้น
2. ต้องเขียนไว้ร่วมกับไฟล์อื่น เช่น script หรือ function ไม่สามารถแยกเขียนเป็นไฟล์เดียวได้

Anonymous function มีรูปแบบการเขียนดังนี้

functionName = @(inputA, inputB, ... , inputN)    Equation ;

ตัวอย่างเช่น
clc;clear;close all;

%Anonymous function
Trigone = @(A,B)  sqrt((A^2)+(B^2));

%How to use 
C = Trigone(10,10)

%Anonymous function
A = '3*x + 2*y - 3';
calEQ = @(str,x,y) eval(str);

%How to use 
D = calEQ(A,1,2)


ผลรัน

C =

   14.1421


D =

     4

ในตัวอย่างนี้มี Anonymous function อยู่ 2 ฟังก์ชัน ฟังก์ชันแรกชื่อว่า Trigone และฟังก์ชันที่สองชื่อว่า calEQ ซึ่งทั้ง 2 ฟังก์ชันนี้จะต้องสร้างขึ้นมาก่อน ถึงจะเรียกใช้งานได้

ฟังก์ชันแรก Trigone
รับ input 2 ตัวคือ A กับ B เพื่อนำไปคำนวณในสมการ sqrt((A^2)+(B^2)) ซึ่งมี A และ B เป็นตัวแปร (ดังนั้นสมการนี้มี 2 ตัวแปร)

ฟังก์ชันสอง calEQ
รับ input 3 ตัว คือ str , x และ y เพื่อนำไปคำนวณในสมการข้างหลังคือ eval(str) แต่ eval ไม่ใช่สมการแต่เป็นคำสั่งประมวลผลสตริง (ข้อความ) อย่างเช่น

A = ' 1+1 ';

ตัวแปร A ไม่ได้มีค่าเท่ากับ 2 แต่เก็บสตริงคำว่า ' 1+1 ' เอาไว้ ถ้าเราอยากคำนวณสตริงชุดนี้ให้ออกมาเป็นคำตอบ เราจะใช้คำสั่ง eval ดังนี้

B = eval(A)

B =

     2

และในตัวอย่างนี้สตริงที่อยู่ในคำสั่ง eval คือตัวแปร str ซึ่งในตัวอย่างนี้เราเรียกใช้งานฟังก์ชัน calEQ แบบนี้

D = calEQ(A,1,2)

ดังนั้น str ก็คือ A ซึ่งเก็บสตริง '3*x + 2*y - 3' ดังนั้นเมื่อ eval(str) เสร็จแล้ว เราก็จะเห็นว่ามันจะกลายเป็นสมการที่มี 2 ตัวแปรคือ x กับ y ดังนั้นฟังก์ชัน calEQ จึงรับ input เพิ่มอีก 2 ตัวเพื่อนำมาเป็นค่า x และ y ในการคำนวณ ซึ่งในตัวอย่างนี้ x มีค่าเท่ากับ 1 และ y มีค่าเท่ากับ 2 (เพราะ x เป็น input ตัวที่ 2 และ y เป็น input ตัวที่ 3)

ประเภทที่ 7 ฟังก์ชันเรียกตัวเอง

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

ตัวอย่างที่ 1
function cDown(X)
if(X>0)
    disp(X)
    pause(1);
    cDown(X-1);
end
end

ตัวอย่างผลรัน

>> cDown(5)
     5

     4

     3

     2

     1


ตัวอย่างที่ 2
ใช้ฟังก์ชันเรียกตัวเอง เพื่อแก้สมการ ด้วยวิธีการเพิ่ม หรือลด ค่าของคำตอบไปเรื่อยๆ จนกว่าจะได้คำตอบที่เข้าใกล้คำตอบจริงมากที่สุด (โดยใช้ค่า error ที่ยอมรับได้เป็นเกณฑ์)

function varargout = fcnex7(EXP,varargin)
switch(nargin)
    case {0,1}
        fcnex7(EXP,0);
    case 2
        val1 = varargin{1};
        calEQ = @(str,X) eval(str);
        Y1 = calEQ(EXP,val1);
        Y2 = calEQ(EXP,val1+1);
        
        %Answer
        if(Y1<0.1 && Y1>-0.1)
            X = varargin{1};
            disp([EXP ' = 0']);
            disp(['X = ' num2str(X)]);
            assignin('base','XssssX',X);
        else
            %Answer of X is Y=0
            if(Y1<Y2 && Y1>0)
                fcnex7(EXP,val1-0.01);  %Call itself
            end
            if(Y1<Y2 && Y1<0)
                fcnex7(EXP,val1+0.01);  %Call itself
            end
            %------
            if(Y1>Y2 && Y1>0)
                fcnex7(EXP,val1+0.01);  %Call itself
            end
            if(Y1>Y2 && Y1<0)
                fcnex7(EXP,val1-0.01);  %Call itself
            end
        end
    otherwise
        fcnex7(EXP,0);
end

if(nargout==0)
    %empty
end
if(nargout>0)
    varargout{1} = evalin('base','XssssX');
    evalin('base','clear XssssX');
end
end

ตัวอย่างผลรัน

>> SX = fcnex7('3*X+2')
3*X+2 = 0
X = -0.64

SX =

   -0.6400

>> SX = fcnex7('12*X-5')
12*X-5 = 0
X = 0.41

SX =

    0.4100

ประเภทที่ 8 Private function

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

ในตัวอย่างนี้ผมจะเอาฟังก์ชัน fcnex5 มาแยกออกเป็น private function ให้ดูนะครับ ส่วนการทำงานของโปรแกรมนั้นเหมือนเดิมทั้งหมด

*ชื่อไฟล์ fcnex8.m

%% Main function ---------------------------------------------------
function varargout = fcnex8(varargin)
if(nargin<3)
    varargout{1} = 0;
    varargout{2} = 0;
end
if(nargin==3)
    %Call private function
    [A,B] = TriAngle(varargin{1},varargin{2},varargin{3});  
    varargout{1} = A;
    varargout{2} = B;
end
if(nargin==4)
    %Call private function
    [A,B] = Squares(varargin{1},varargin{2},varargin{3},varargin{4});
    varargout{1} = A;
    varargout{2} = B;
end
if(nargout>2)
    for m=3:nargout
        varargout{m} = 0;
    end
end
end

%% Private function -------------------------------------------------
function [Area1,Area2] = TriAngle(A,B,C)
S = (A+B+C)/2;
Area1 = sqrt(S*(S-A)*(S-B)*(S-C));
Area2 = convert2Rai(Area1);     %Call private function
end

function [Area1,Area2] = Squares(A,B,C,D)
S = (A+B+C+D)/2;
Area1 = sqrt((S-A)*(S-B)*(S-C)*(S-D));
Area2 = convert2Rai(Area1);     %Call private function
end

function Area2 = convert2Rai(Area1)
Area2 = Area1/1600;
end

ในตัวอย่างนี้มี private function อยู่ 3 ฟังก์ชัน ได้แก่ TriAngle, Squares และ convert2Rai
จากตัวอย่างนี้จะเห็นว่าทุกฟังก์ชันที่อยู่ในไฟล์นี้ ไม่ว่าจะเป็น Main function หรือ Private function ต่างเรียกใช้งาน private function ได้ทั้งหมด หรือก็คือ private function ก็สามารถเรียกใช้งาน private function ได้ ขอแค่ฟังก์ชันทั้งหมดอยู่ในไฟล์เดียวกัน

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

ตัวอย่างผลรัน

>> [A,B] = fcnex8(10,10,10)

A =

   43.3013


B =

    0.0271

รูปสามเหลี่ยมขนาด 10x10x10 เมตร มีพื้นที่ 43.3013 ตารางเมตร หรือ 0.0271 ไร่








>> [A,B] = fcnex8(10,10,10,10)

A =

   100


B =

    0.0625

รูปสี่เหลี่ยมขนาด 10x10x10x10 เมตร มีพื้นที่ 100 ตารางเมตร หรือ 0.0625 ไร่

ถ้าหากเราลองเรียก Private function จาก Command window หรือโปรแกรมอื่นๆ ก็จะพบข้อความแจ้งเตือน error ดังนี้

>> [A,B] = TriGone(10,10,10)
Undefined function or variable 'TriGone'.

ดังนั้น Private function คือฟังก์ชันที่มีแค่ตัวมันเท่านั้นที่เรียกใช้งานได้

ประเภทที่ 9 ฟังก์ชันซ้อน

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

*ชื่อไฟล์ fcnex9.m
function fcnex9(HR,MN,SS)
%Create UI
clc;close all;
f = figure('tag','timeshow','menubar','none','numbertitle',...
    'off','Name','Count down clock');
Pos = f.Position;
Pos(3) = 700;
Pos(4) = 250;
f.Position = Pos;
t = text(0,0.5,'00:00:00','fontsize',100);
axis off;

%Create timer
delete(timerfindall);
tm = timer('StartDelay',1,'Period',1);
tm.TimerFcn = @UpdateClock;
tm.StartFcn = @UpdateClock;
tm.StopFcn = @EndTimer;
tm.ExecutionMode = 'fixedRate';
start(tm);

    function UpdateClock(varargin)
        if(SS<0)
            SS = 59;
            MN = MN-1;            
        end
        if(MN<0)
            MN = 59;
            HR = HR-1;
        end
        if(HR==0 && MN==0 && SS==0)
            stop(tm);
        end
        strTM = sprintf('%02d:%02d:%02d',HR,MN,SS);
        t.String = strTM;
        
        if(HR==0 && MN==0 && SS<10)
            if(mod(SS,2)==0)
                f.Color = [0.94, 0.94, 0.94];
                t.Color = [0, 0, 0];
            else
                f.Color = [1, 0, 0];
                t.Color = [1, 1, 1];
            end
        end
        SS = SS-1;        
    end

    function EndTimer(varargin)
        delete(timerfindall);
    end
end

ในตัวอย่างนี้ เป็นโปรแกรมนาฬิกานับถอยหลัง ในรูปแบบที่เป็น UI โดยมีฟังก์ชัน fcnex9 เป็นฟังก์ชันหลัก และมีฟังก์ชันซ้อนคือ ฟังก์ชัน UpdateClock และ EndTimer โดยทั้ง 2 ฟังก์ชันนี้มีการเรียกใช้ตัวแปร HR, MN, SS, f, t จากฟังก์ชันหลัก

ผู้อ่านสังเกตุดูนะครับว่าขอบเขตของฟังก์ชัน UpdateClock และ EndTimer (นับจากคำว่า function ไปถึง end) จะเริ่มต้นฟังก์ชันและจบฟังก์ชันในฟังก์ชันหลักเสมอ ดังนั้นเราจึงเรียกฟังก์ชันประเภทนี้ว่าฟังก์ชันซ้อนนั่นเองครับ

ตัวอย่างผลรัน




จบเรื่องฟังก์ชัน

ความเห็น

โพสต์ยอดนิยมจากบล็อกนี้

การแก้สมการ Differential ด้วย MATLAB

การหาค่าเฉลี่ยโดยไม่ต้องเก็บค่า

ว่าด้วยเรื่องของ ERROR