Erlang - การเรียกซ้ำ
การเรียกซ้ำเป็นส่วนสำคัญของ Erlang ก่อนอื่นมาดูกันว่าเราจะใช้การเรียกซ้ำอย่างง่ายได้อย่างไรโดยใช้โปรแกรมแฟกทอเรียล
ตัวอย่าง
-module(helloworld).
-export([fac/1,start/0]).
fac(N) when N == 0 -> 1;
fac(N) when N > 0 -> N*fac(N-1).
start() ->
X = fac(4),
io:fwrite("~w",[X]).
สิ่งต่อไปนี้ต้องสังเกตเกี่ยวกับโปรแกรมข้างต้น -
ก่อนอื่นเราจะกำหนดฟังก์ชันที่เรียกว่า fac (N)
เราสามารถกำหนดฟังก์ชันการเรียกซ้ำได้โดยเรียก fac (N) แบบเรียกซ้ำ
ผลลัพธ์ของโปรแกรมข้างต้นคือ -
เอาต์พุต
24
แนวทางปฏิบัติในการเรียกซ้ำ
ในส่วนนี้เราจะทำความเข้าใจโดยละเอียดเกี่ยวกับการเรียกซ้ำประเภทต่างๆและการใช้งานใน Erlang
การเรียกซ้ำความยาว
แนวทางที่เป็นประโยชน์มากขึ้นในการเรียกซ้ำสามารถดูได้จากตัวอย่างง่ายๆซึ่งใช้เพื่อกำหนดความยาวของรายการ รายการสามารถมีได้หลายค่าเช่น [1,2,3,4] ลองใช้การเรียกซ้ำเพื่อดูว่าเราจะได้ความยาวของรายการได้อย่างไร
Example
-module(helloworld).
-export([len/1,start/0]).
len([]) -> 0;
len([_|T]) -> 1 + len(T).
start() ->
X = [1,2,3,4],
Y = len(X),
io:fwrite("~w",[Y]).
สิ่งต่อไปนี้ต้องสังเกตเกี่ยวกับโปรแกรมข้างต้น -
ฟังก์ชั่นแรก len([]) ใช้สำหรับเงื่อนไขกรณีพิเศษหากรายการว่างเปล่า
[H|T] รูปแบบเพื่อจับคู่กับรายการขององค์ประกอบตั้งแต่หนึ่งรายการขึ้นไปเนื่องจากรายการความยาวหนึ่งจะถูกกำหนดเป็น [X|[]] และรายการความยาวสองจะถูกกำหนดเป็น [X|[Y|[]]]. โปรดทราบว่าองค์ประกอบที่สองคือรายการเอง ซึ่งหมายความว่าเราจำเป็นต้องนับหนึ่งเท่านั้นและฟังก์ชันสามารถเรียกตัวเองในองค์ประกอบที่สองได้ กำหนดให้แต่ละค่าในรายการนับเป็นความยาว 1
ผลลัพธ์ของโปรแกรมข้างต้นจะเป็น -
Output
4
การเรียกซ้ำหาง
หากต้องการทำความเข้าใจว่าการเรียกซ้ำหางทำงานอย่างไรเรามาทำความเข้าใจว่าโค้ดต่อไปนี้ในส่วนก่อนหน้าทำงานอย่างไร
Syntax
len([]) -> 0;
len([_|T]) -> 1 + len(T).
คำตอบของ 1 + len (Rest) ต้องการคำตอบของ len (Rest) จึงจะพบ ฟังก์ชั่น len (Rest) นั้นเองก็ต้องการผลลัพธ์ของการเรียกใช้ฟังก์ชันอื่น การเพิ่มจะซ้อนกันจนกว่าจะพบรายการสุดท้ายจากนั้นจะคำนวณผลลัพธ์สุดท้ายเท่านั้น
การเรียกซ้ำหางมีจุดมุ่งหมายเพื่อกำจัดการดำเนินการที่ซ้อนกันนี้โดยการลดจำนวนลงเมื่อเกิดขึ้น
เพื่อให้บรรลุเป้าหมายนี้เราจะต้องถือตัวแปรชั่วคราวพิเศษเป็นพารามิเตอร์ในฟังก์ชันของเรา ตัวแปรชั่วคราวดังกล่าวบางครั้งเรียกว่าตัวสะสมและทำหน้าที่เป็นที่เก็บผลลัพธ์ของการคำนวณของเราเมื่อเกิดขึ้นเพื่อ จำกัด การเติบโตของการโทรของเรา
ลองดูตัวอย่างของการเรียกหางซ้ำ -
Example
-module(helloworld).
-export([tail_len/1,tail_len/2,start/0]).
tail_len(L) -> tail_len(L,0).
tail_len([], Acc) -> Acc;
tail_len([_|T], Acc) -> tail_len(T,Acc+1).
start() ->
X = [1,2,3,4],
Y = tail_len(X),
io:fwrite("~w",[Y]).
ผลลัพธ์ของโปรแกรมข้างต้นคือ -
Output
4
ทำซ้ำ
ลองดูตัวอย่างของการเรียกซ้ำ คราวนี้เรามาเขียนฟังก์ชันที่ใช้เลขจำนวนเต็มเป็นพารามิเตอร์ตัวแรกจากนั้นคำอื่น ๆ เป็นพารามิเตอร์ที่สอง จากนั้นจะสร้างรายการสำเนาของคำศัพท์ตามที่ระบุโดยจำนวนเต็ม
มาดูตัวอย่างกันว่าจะเป็นอย่างไร -
-module(helloworld).
-export([duplicate/2,start/0]).
duplicate(0,_) ->
[];
duplicate(N,Term) when N > 0 ->
io:fwrite("~w,~n",[Term]),
[Term|duplicate(N-1,Term)].
start() ->
duplicate(5,1).
ผลลัพธ์ของโปรแกรมข้างต้นจะเป็น -
เอาต์พุต
1,
1,
1,
1,
1,
รายการการกลับรายการ
ไม่มีขอบเขตที่คุณสามารถใช้การเรียกซ้ำใน Erlang ได้ มาดูกันอย่างรวดเร็วว่าเราสามารถย้อนกลับองค์ประกอบของรายการโดยใช้การเรียกซ้ำได้อย่างไร สามารถใช้โปรแกรมต่อไปนี้เพื่อทำสิ่งนี้ให้สำเร็จ
ตัวอย่าง
-module(helloworld).
-export([tail_reverse/2,start/0]).
tail_reverse(L) -> tail_reverse(L,[]).
tail_reverse([],Acc) -> Acc;
tail_reverse([H|T],Acc) -> tail_reverse(T, [H|Acc]).
start() ->
X = [1,2,3,4],
Y = tail_reverse(X),
io:fwrite("~w",[Y]).
ผลลัพธ์ของโปรแกรมข้างต้นจะเป็น -
เอาต์พุต
[4,3,2,1]
สิ่งต่อไปนี้ต้องสังเกตเกี่ยวกับโปรแกรมข้างต้น -
เราใช้แนวคิดของตัวแปรชั่วคราวอีกครั้งเพื่อจัดเก็บแต่ละองค์ประกอบของรายการในตัวแปรที่เรียกว่า Acc
เราก็โทร tail_reverse วนซ้ำ แต่คราวนี้เรามั่นใจว่าองค์ประกอบสุดท้ายถูกใส่ในรายการใหม่ก่อน
จากนั้นเราจะเรียกใช้ tail_reverse ซ้ำสำหรับแต่ละองค์ประกอบในรายการ