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

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

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

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

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

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

  • ۰
  • ۰

تصمیم گرفتم مثالهای کتاب Exercises for programmer را به زبان ارلنگ انجام دهم و پس اطمینان از درستی آنها، منتشر کنم. مثال اول که بسیار ساده بود. از کاربر اسمش را سوال کنم و عبارت Hello را در ابتدای آن گذاشته و سپس در خروجی چاپ کند.

 کد بشرح زیر است:

-module(ex1).

-export([hello/0]).

hello() ->
    {ok,S} = io:fread("What is your name? ","~s"),
    io:fwrite("Hello ~s~n",[S]).
  • مهدی حسینی مقدم
  • ۰
  • ۰

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

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).

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

 

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

ارلنگ هم مانند بسیاری از زبان های برنامه نویسی عبارت های کنترلی دارد هرچند که با استفاده از تطبیق الگو و استفاده از توابع با بخش های مجزا تقریبا نیازی با آن ندارید. برای نمونه ارلنگ case را معرفی کرده است که کاربردی کاملا شبیه switch در زبانهای خانواده C دارد.

case Condition of

               true -> doSomethings;

               false -> doNothing

end;

در case  ابتدا شرطی مورد بررسی قرار می گیرد و مطابق با مقدارش قسمتی از case اجرا می شود. برخلاف زبانهای برنامه نویسی خانواده C  شما نیازی به break ندارید و تنها بخشی که مطابقت یافت اجرا می شود.

ارلنگ if  هم دارد. کار با آن ساده است تنها باید بخاطر داشته باشید که در زبان ارلنگ لااقل یکی از بخش ها باید true  باشد در غیر ایصورت با پیغام خطا روبرو می‌شوید.

max(X, Y) ->

               if

                              X > Y -> X;

                              X < Y -> Y;

                              true -> nil

               end.

همانطور که اشاره شد در بسیاری از موارد نیازی به استفاده از عبارات کنترلی نیست. برای نمونه ارلنگ عملگرهایی را برای کار برروی لیست در خود دارد که شما براحتی می توانید با انواع لیست ها کار کنید. فرمت آنها مطابق دروس جبر دوره دبیرستان است. مثلا زمانی که بخواهید کلیه اعضای یک لیست را دو برابر کرده در لیست جدیدی بریزید به قسم زیر عمل می کنید:

[2*X || <- L]

عبارت بالا اینطور خوانده می شود: به ازای هر X  از لیست L  دو برابر X  را حساب کن. حاصل لیستی ازمقادیر جدید خواهد بود. مثال زیر به خوبی نمایش گر این کاربرد خواهد بود

L = [1,2,3,4,5].

L2=[2*X||X <- L]

L2=[2,4,6,8,10]

 

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

گاهی اوقات نیاز دارید توابعی را برای استفاده لحظه ای تعریف کنید و یا برای نمونه تابعی را به عنوان متغیر به تابع دیگری بفرستید. برای این منظور در ارلنگ می توانید توابع بی نام تعریف کنید برای این کار مطابق زیر عمل کنید:

Double = fun(X) -> 2 * X end.

در مثال فوق Double  یک متغیر است که تابعی را در خود جا داده است. این تابع بی نام ورودی خود را دوبرابر می کند. می توان از Double مانند تابع معمولی استفاده کرد برای نمونه می توان از Double با ورودی 10 خروجی 20 را گرفت. استفاده از توابع بی نام محدودیتی ندارد و شما می توانید در داخل توابع بی نام از توابع بی نام استفاده کنید. عدد ورودی را در در عدد دیگری ضرب می کند. خروجی تابع اول تابعی است که عمل ظرب را انجام می دهد. خروجی تابع دوم عدد مورد نظر است:

Mult = fun(Times) -> ( fun(X) -> X * Times end ) end.

Triple = Mult(3).

Triple(5).  % 15

کنترل ورودی توابع از طریق گارد:

گاهی اوقات نیاز دارید مقادیر ورودی توابع را چک کنید بدون آنکه وارد تابع شوید. بنابراین در ارلنگ در صورت عدم تطابق به قسمت بعدی تابع جابجا می شوید. برای اینکار از when استفاده می کنیم. یعنی زمانی از این قسمت تابع استفاده کن که شرط مقابل برقرار باشد. به مثال زیر توجه کنید:

max(X, Y) when X > Y -> X;

max(X, Y) -> Y.

تابع بالا بیشینه دو عدد را بر می گرداند. قسمت اول چنانچه X>Y باشد تابع X  را بر می گرداند. چنانچه نبود تابع در قسمت دوم اجرا می شود که Y  را بر می گرداند. مشاهده می کنید که در ارلنگ عملا بجای استفاده از if که در زبان های رایج کاربرد دارد می تواند در قسمت ورودی توابع را بررسی کرد. با عبارت when  و شرط بعدی اش گارد گفته می شود. گارد ها می تواند شامل چند پیش شرط باشند که با ‘,’ از هم جدا می شوند و معنی AND را می دهد.

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

کدهای زبان برنامه نویسی ارلنگ در درون واحدی بنام ماژول قرار می گیرد تقریبا تمامی توابع در داخل ماژول نوشته می‌شوند. ماژول ها در داخل فایلهایی با پسوند .erl ذخیره می‌شوند. ماژول ها برای اجرا نیاز به کامپایل شدن دارند. لازم به ذکر است فایل های کامپایل شده دارای پسوند .beam می باشند. در مثال زیر تابعی بنام area تعریف شده که بسته به نوع ورودی مساحت شکل مورد نظر را محاسبه می کند.

-module(geometry).

-export([area/1]).

area({rectangle, Width, Height}) -> Width*Height;

area({circle, R}) -> 3.14 * R * R.

لازم به ذکر است area یک تابع است و بخشهای مختلف آن با ; از هم جدا می شوند. چنانچه ورودی ما توپلی که عضو اول آن rectangle است، باشد قسمت اول تابع اجرا می گردد و چنانچه circle باشد قسمت دوم. نکته اینجاست که تابع در حالتی که ورودی دارای شکل متفاوتی باشد تعریفی ندارد و کاربر پیغام خطا خواهد گرفت. برای غلبه بر آن می تواند قسمت کلی در انتهای تابع قرار داد:

area({rectangle, Width, Height}) -> Width*Height;

area({circle, R}) -> 3.14 * R * R;

area(_) -> error.

توجه داشته باشید که _ به معنی هر ورودی می باشد. توابع از بالا به پایین اجرا می شوند و بنابراین چنانچه ورودی مطابق با دو قسمت اول نبود قسمت سوم اجرا خواهد شد. جابجایی قسمتهای تابع نتیجه متفاوتی خواهد داد.

فایل فوق بنام geometry.erl ذخیره می گردد. برای کامپایل کردن آن کافی است وارد محیط ارلنگ شده و با استفاده از دستور زیر فایل را کامپایل کنیم:

c(geometry).

برای استفاده از توابع فایل بصورت زیر عمل می کنیم:

geometry:area({rectangle, 10, 50}).

geometry:area({circle, 2}).

به ترتیب باید 500 و 12.56 را بعنوان جواب دریافت کنید.  

چنانچه شما دانشی در زمینه زبانهای برنامه نویسی شی گرا داشته باشید با نوع داده‌ و توابع عمومی و خصوصی آشنا هستید. توابع عمومی توابعی هستند که در بیرون از کلاس قابل دسترسی هستند حال آنکه توابع خصوصی تنها در داخل کلاس می توانند مورد استفاده قرار گیرند. چنین محدود سازی در ارلنگ با کمک export صورت می گیرد. در قسمت export شما لیستی از توابعی را که مایلید از بیرون قابل اجرا باشند را وارد می کنید. توابع بصورت نام / تعداد ورودی نوشته می شوند. از آنجایی که تابع area تنها یک ورودی می گیرد نام کامل آن بصورت area/1 خواهد بود.

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

در این بخش تصمیم دارم تا در مورد انواع فرمت های داده در ارلنگ صحبت کنم. ابتدا با کامنت شروع می کنم. همانطور که ممکن است به مفهوم کامنت آشنا باشید، کامنت ها بخش هایی از کد هستند که نوشته می شوند اما توسط مفسر و کامپایلر خوانده نمی شوند. کامنت ها تنها برای خوانا کردن کد و توضیحات داخل کد برای برنامه نویس استفاده می شود. کامنت در زبان ارلنگ با "%" شروع می شود بدین معنی که خطی با این علامت آغاز شود کامنت خواهد بود.

% this is comment

ارلنگ دارای محیطی بنام REPL است که چیزی شبیه شل در لینوکس و محیط کامند در ویندوز است. برای استفاده از آن در لینوکس فرمان erl را تایپ کنید. در ویندوز کافی است این برنامه را از شاخه برنامه های خود اجرا نمائید.

متغیر ها در زبان ارلنگ با حروف بزرگ آغاز می شود. در ارلنگ در پایان هر عبارت باید "." گذاشت. برای مثال خط زیر مقدار 7 را به متغیر X نسبت می دهد:

X = 7.

شما به هر متغیر می توانید تنها یکبار مقدار بدهید، این نخستین تفاوت برنامه نویسی فانکشنال با زبانهای شی گرا می باشد. برای مثال چنانچه شما بخواهید مقدار 9 را به X  بدهید پیغام خطای زیر را دریافت خواهید کرد:

 

** exception error: no match of right hand side value 9

برای مقدار دهی نیاز به مشخص کرد نوع داده نیست بنابرین شما می توانید هر مقداری خواستید به هر متغیر بدهید برای مثال می توانید مقدار اعشاری وارد نمائید.

اتم ها نوع دیگری از داده هستند که در ظاهر شبیه کلمه هستند اما مقدار رشته ئی ندارند. اتم ها ثابت هایی هستند که می توانید از آنها استفاده کنید. در استفاده از آن ها دقت کنید زیرا اتم ها توسط آشغال جمع کن ارلنگ حذف نمی شوند و ابدی هستند.

Y = hello.

توپل ها گروهی از داده ها را در خود جای می دهند برای ایجاد آنها از { استفاده کنید. برای نمونه خط زیر یک توپل که حاوی دو مقدار عددی است را نشان می دهد:

Point = {7,8}.

قابلیت زیبای ارلنگ استخراج مقادیر از عبارات است برای مثال شما می توانید با استفاده از دستور زیر مقادیر داخل Point را در متغیر های جدید قرار دهید:

{X,Y} = Point.

با انجام این کار X برابر 7 و Y برابر 8 خواهد بود.

لیست ها آرایه هایی از مغادیر مختلف هستند. برای ایجاد یک لیست کافیست از ] استفاده کنید. برای مثال مانند زیر عمل کنید:

L = [1,2,3,4,5]

با استفاده از | شما می توانید راس یک لیست را از باقی آن جدا کنید برای مثال:

[H|T] = L.

که در این صورت خواهیم داشت:

H = 1 , T = [2,3,4,5]

ارلنگ نوع رشته ندارد و این یکی از ضعف های این زبان است در حقیقت رشته ها با استفاده از لیستی از اعداد که شماره حروف در جدول کاراکتر ها هستند تعریف می شوند:

[72, 101, 108, 108, 111] = "Hello".

 

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

امروزه انواع و اقسام مختلف ویرایش گر ها، کار را برای برنامه نویس ها آسان تر کرده است. البته من به شخصه ترجیح می دهم لااقل در مرحله یادگیری از ابزارهای ساده تر استفاده کنم. دلیلش این است که بیشتر به جزئیات روند کامپایل در گیر می شوم و بهتر یاد می گیرم.

برای برنامه نویسی به زبان ارلنگ و الیکسیر ویرایش گر ها و محیط های توسعه زیادی موجود است. برای مثال اینتلی جی افزونه های مربوط به ارلنگ را دارد که امکانات خوبی ارائه می دهد. با این وجود من سعی می کنم از ویرایش گر VIM استفاده کنم و با استفاده از کمی تغییرات آن را برای محیط ارلنگ آماده کنم. 

ابتدا ویم را با استفاده از فرمان زیر نصب کنید:

sudo apt-get install vim

ویرایشگر ویم قابلیت افزدون ماژول های مختلف را دارد برای این منظور ابتدا نیاز است یک ماژول خاص جهت افزودن سایر ماژول های برنامه نویسی به آن اضافه کنیم. انتخاب من pathogen است. برای نصب آن فرامین زیر اجرا کنید:

mkdir -p ~/.vim/autoload ~/.vim/bundle && \

curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim

سپس فایل vimrc. را که در پوشه خانه شما قرار دارد را ویرایش کنید. برای رفتن به خانه از فرمان cd ~ استفاده کنید. چنانچه این فایل موجود نبود آن را ایجاد کنید و سپس محتویات زیر را آن را قرار دهید:

execute pathogen#infect()

syntax on

filetype plugin indent on

حالا همه چیز آماده است برای نصب ماژول ارلنگ. برای این منظور فرامین زیر را اجرا کنید:

cd ~/.vim/bundle && \

git clone https://github.com/jimenezrick/vimerl.git

حال هر زمان شما فایلی را با پسوند erl باز کنید VIM کلمات و علائم زبان ارلنگ را با رنگ منحصر بفرد نشان می دهد.

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

از آنجایی که محیط تست برنامه نویسی که من انتخاب کردم لینوکس رسپبیان است که البته خود یک توزیع مبتنی بر دبیان می باشد به فکر افتادم تا مراحل نصب این لینوکس را بر روی برد رسپبری مکتوب کنم. با در نظر گرفتن این نکته که نصب این توزیع در حالت معمولی تفاوت چندانی با نصب سایر توزیع های لینوکس ندارد تنها به ارائه شیوه نصب آن در حالت "ساکت" بسنده می کنم.

حالت ساکت به حالتی گفته می شود که شما می خواهید بدون استفاده از صفحه کلید و موشواره و البته صفحه نمایش اقدام به نصب و راه اندازی رسپبیان کنید. برای این کار مراحل زیر را انجام دهید:

1-      کارت حافظه مورد نظر خود با استفاده از فرمتر اس دی فرمت کنید. از فرمت کردن کارت با استفاده از فرمتر ویندوز خودداری کنید.

2-      فایل نوب را از سایت رسپبری دانلود کنید. سپس این فایل را با استفاده از Winrar و یا سایر نرم افزارهای فشرده سازی باز کنید.

3-      با شاخه‌ی \os\Raspbian رفته و سپس فایل flavours.json را به قسمی ویرایش کنید که تنها بخش raspbian  داشته باشد. درنظر داشته باشید که چنانچه بخشی با تگ Raspbian – Boot to Scratch وجود داشت آن را نیز حذف کنید. محتویات فایل باید چیزی شبیه زیر باشد.

{

"flavours": [

{

"name": "Raspbian",

"description": "A Debian wheezy port, optimised for the Raspberry Pi"

}]}

4-      در پوشه اصلی فایل recovery.cmdline را برای ویرایش باز کنید و سپس silentinstall را در انتهای آن بیافزایید. پس از اضافه کرده آن باید چیزی شبیه به خط زیر داشته باشید.

runinstaller quiet vt.cur_default=1 coherent_pool=6M elevator=deadline silentinstall

5-      محتویات باز شده و ویرایش شده را روی کارت حافظه کپی کنید.

6-      کارت حافظه را به برد متصل کرده و برد را روشن کنید و به مدت یک تا دو ساعت به حال خود بگذارید.

7-      برد را با استفاده از کابل به شبکه خود متصل کنید.

8-      با استفاده از پوتی و با وارد کردن IP برد بصورت SSH لاگین کنید و لذت ببرید. لازم به ذکر است حساب کاربری پیش فرض pi و پسورد raspberry می باشد. ضمنا برای بدست آوردن IP برد می توانید از نرم افزارهایی نظیر IP  اسکنر استفاده کنید.

 

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