برنامه نویسی به زبان ارلنگ

ارلنگ زبان برنامه نویسی تابعی و همروند است

برنامه نویسی به زبان ارلنگ

ارلنگ زبان برنامه نویسی تابعی و همروند است

برنامه نویسی به زبان ارلنگ

در این وبلاگ مطالب مربوط به ارلنگ و الیکسیر منتشر خواهد شد

۳ مطلب در اسفند ۱۳۹۴ ثبت شده است

  • ۰
  • ۰

حتما شما با سری فیبوناچی در دوران دبیرستان آشنا شده‌اید. بطور اختصار سری فیبوناچی از فرمول زیر بدست می‌آید:

f(n) = f(n-1) + f(n-2)

هر عنصر در این سری حاصل جمع دو عنصر ما قبل است. البته مقدار برای n برابر با صفر و یک هر دو یک است. برای نمونه سری زیر، فیبوناچی است:

1,1,2,3,5,8,13,21

تصور کنید می‌خواهید با استفاده از ارلنگ عنصر n یک سری را محاسبه کنید. اگر بخواهیم فرمول ریاضی آورده شده را به یک برنامه ارلنگ تبدیل کنیم کار سختی در پیش نخواهیم داشت.

-module(fib).
-export([fib/1]).

fib(0) -> 0;
fib(1) -> 1;
fib(N) -> fib(N-1) + fib(N-2).

خیلی ساده در نمونه کد بالا یک ماژول به اسم fib تعریف شد. تابعی به همین نام نیز تعریف شد که برای مقدار ورودی 0 و 1 خروجی 1 را بر می گردان. برای سایر موارد درست مانند فرمول ریاضی عمل می‌کند. هرگاه تابعی در درون خود خودش را صدا بزند با آن تابع بازگشتی می‌گویند. برای کامپایل کردن کد بالا ابتدا وارد شل ارلنگ شده و کد مربوط را با روش زیر کامپایل و سپس اجرا می‌کنیم:

1>    c(fib.erl).
{ok,fib}

2>    [fib:fib(1), fib:fib(2), fib:fib(3), fib:fib(4), fib:fib(5), fib:fib(6)].
[1,1,2,3,5,8]

خب بنظر کار ما تمام است. اما تابع تعریف شده مشکلاتی دارد. از آنجایی که برای نمونه (fib(n-1) = fib(n-2) + fib(n-3 پس می توان نتیجه گرفت که مقدار(fib(n-2 دوبار محاسبه می‌شود و این مسئله بار زیادی برروی سیستم می‌گذارد.

راه حال بهتر استفاده از نوع خاصی از توابع بازگشتی است که به آنها tail-recursive گفته می‌شود. شاید بهترین ترجمه برای آن بازگشتی انتهایی باشد. در این توابع باز هم خود تابع صدا زده می‌شود اما به عنوان آخرین عبارت و کنترل هرگز به تابع صدا زننده بر نمی‌گردد. با استفاده از این تعریف تابع بصورت زیر باز تعریف می‌شود:

-module(fib2).
-export([fib/1]).

fib(N) -> fib_iter(N, 0, 1).

fib_iter(0, Result, _Next) -> Result;
fib_iter(Iter, Result, Next) when Iter > 0 -> fib_iter(Iter-1, Next, Result+Next).

برای مقایسه سرعت دو تابع می‌شود از تابع tc در ماژول timer با گونه زیر استفاده کرد. نتیجه برای هر دو تابع قابل مشاهده و مقایسه است.

 

timer:tc(fib, fib, [40]). % {556300, 102334155}

timer:tc(fib2,fib,[40]). %{0, 102334155}

مولفه اول در توپل نشان دهنده زمان اجرا بر مبنای میلی ثانیه است. همانطور که می بینید تفاوت فاحش است.

لازم به ذکر است تا کنون JVM و NET. از بازگشتی انتهایی پشتیبانی نمی‌کنند هرچند بنابه گفته مایکروسافت در سال آینده این قابلیت اضافه خواهد شد.

  • مهدی حسینی مقدم
  • ۰
  • ۰

مدلی که ارلنگ برای همروندی و همزمانی استفاده می کند بر اساس مدل بازیگر (actor model) است. تمام چیزی که برای نوشتن نرم افزار های همروند در ارلنگ نیاز دارید در سه چیز خلاصه می شود: ساخت پروسه، ارسال پیام به پروسه و دریافت و خواندن پروسه ارسال شده می‌باشد. هر پروسه در ارلنگ دارای مفهومی بنام صندوق پستی است. پیام های ارسالی به هر پروسه درون این صندوق ذخیره می شود و پروسه می‌تواند براساس صلاح دید خود پیامهایش را بخواند و یا دور بریزد. برای ساختن پروسه جدید در ارلنگ از روش زیر می‌توان استفاده کرد:

F = fun() -> 2 + 2 end.

spawn(F).

Spawn به عنوان خروجی شناسه پروسه که به آن PID می گویند را بر می‌گرداند. از این PID می‌توان برای ارسال پیام به پروسه استفاده کرد. ارسال پیام به پروسه به عملگر "!" امکان پذیر است.

Pid ! hello.

زمانی که پیام ارسال شد گیرنده با استفاده از receive می تواند مقدار پیام را بخواند. مثال زیر برنامه کوچکی است که منتظر پیام می‌نشیند و حسب نوع و مقدار آن واکنشی را نشان می‌دهد که در این مثال محاسبه‌ی مساحت شکل مورد در خواست است.

-module(calculateGeometry).
-compile(export_all).

calculateArea() ->
	receive
		{rectangle, W, H} -> W * H;
		{circle, R} -> 3.14 * R * R;
		_ -> io:format("We can only calculate area of rectangles or circles.")
	end.

با استفاده از تابع c ماژول را کامپایل کرده و از تابع استفاده می‌کنیم.

c(calculateGeometry).

CalculateArea = spawn(calculateGeometry, calculateArea, []).

CalculateArea ! {circle, 2}.

هر پروسه با استفاده از تابع()self می تواند از شناسه خودش مطلع شود.

 

  • مهدی حسینی مقدم
  • ۰
  • ۰

استثناها توسط سیستم زمانی که مشکلی داخلی پیش می‌آید ایجاد و ارسال می‌شوند. البته شما می‌توانید مستقیما استثنا بسازید و آن را بقرستید. برای این کار در زبان ارلنگ راه های مختلفی وجود دارد شما می توانید تابع(throw(Exception را فراخوانی کنید همچنین می‌توانید با فرخواهنی تابع(exit(Exception نیز این کار را انجام دهید. راه دیگری نیز برای این منظور وجود دارد و آن فراخوانی(erlang:error(Exception می‌باشد.  کد زیر تمام این موارد را در یک تابع نشان می‌دهد:

generate_exception(1) -> a;

generate_exception(2) -> throw(a);

generate_exception(3) -> exit(a);

generate_exception(4) -> {'EXIT', a};

generate_exception(5) -> erlang:error(a).

برای گرفتن استثنا ها نیز در ارلنگ نیز می‌توانید از یکی از دو روش زیر استفاده کنید.

در راه اول شما تابعی که امکان ارسال استثناء دارد را درون عبارت کنترل try  به قسم زیر قرار می‌دهید:

catcher(N) ->

  try generate_exception(N) of

    Val -> {N, normal, Val}

  catch

    throw:X -> {N, caught, thrown, X};

    exit:X -> {N, caught, exited, X};

    error:X -> {N, caught, error, X}

  end.

در راه دوم شما از تابع مورد نظر درون catch قرار می‌دهید.

catcher(N) -> catch generate_exception(N).

به خاطر داشته باشید که زمانی که شما استثناء را دریافت می‌کنید نتیجه تبدیل به فرمت توپل می‌شود.

 

  • مهدی حسینی مقدم