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

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

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

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

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

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

  • ۰
  • ۰

این مثال محاسبه سود است. برای مثال اگر شما 1500 دلار داشته باشید و سالانه 4.3% سود بگیرید بعد از 4 سال پول شما از طریق فرمول زیر به 1758 خواهد رسید.

A = P(1 + rt)

که در آن r همان نرخ سود و t تعداد سال است و P هم همان پول اولیه شماست. در نوشتن این کد به مشکلی برخوردم و آنهم خواندن نوع اعشاری بود که در کد نحوه آن قابل مشاهده است. نکته دیگر استفاده از دیباگر ارلنگ بود. برای این کار باید debug_info را به نحوه زیر به کد اضافه کنید. کار با دیباگر ارلنگ را در زیر کد توضیح خواهد داد.

-module(ex12).
-export([main/0]).
-compile([debug_info]).

main() ->
	try run()
	catch
		error:_Error -> io:format(
			"Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.

run() ->
	{P,I,Y} = readItems(),
	Total = calc_Investment({P,I,Y}),
	io:fwrite(
		"After ~w years at ~w%, the investment will be worth $~w.",
		[Y,I,Total]).

calc_Investment({P,I,Y}) -> 
	P*(1+(I*Y/100)).
	
readItems() ->
	{ok,[P]} = io:fread(
		"Enter the principal: ","~d"),
	%{ok,[I]} = io:fread(
	%	"Enter the rate of interest: ","~f"),
	{I,_} = string:to_float(string:strip(
			io:get_line("Enter the rate of interest: "), right, $\n)),
	{ok,[Y]} = io:fread(
		"Enter the number of years: ","~d"),
	if
		P =< 0 orelse I =< 0 orelse Y =< 0 -> 
			throw(negativeNumber);
		true -> ok
	end,
	{P,I,Y}.

خوب همانطور که مشخص است ابتدا debug_info  را به کد اضافه می‌کنیم. کد را به صورت معمول کامپایل کرده و سپس دیباگر را اجرا می‌کنیم. دیباگر یک محیط گرافیکی دارد که باید ماژول خود را از قسمت Module انتخاب کنیم و سپس تابع اصلی را انتخاب کنیم. می‌توانیم خیلی ساده روی هر خط کد break بگذاریم و خط به خط پیش رویم. کامند ها را بترتیب زیر اجرا می‌کنیم:

163> c(ex12,debug_info).
{ok,ex12}
164> debugger:start().
{ok,<0.552.0>}
  • مهدی حسینی مقدم
  • ۰
  • ۰

در این مثال قیمت سه آیتم از کاربر از ورودی بهمراه تعداد هر کدام را دریافت می‌کنیم و قیمت نهایی را با در نظر گرفتن 5.5% مالیات محاسبه می‌کنیم که بسیار ساده است. در این مثال قسمت دریافت اطلاعات، چاپ خروجی و محاسبات را از هم جدا کردم و ضمنا تابع محابسه بصورت بازگشتی تعریف شده است.

-module(ex10).
-export([main/0]).
-define(TAX, 0.55).

main() ->
	try run()
	catch
		error:_Error -> io:format(
			"Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.

run() ->
	L = readItems(),
	[Sub,Taxed,Total] = calc_tax(L),
	io:fwrite(
		"Subtotal: $~w~nTax: $~w~nTotal: $~w~n",
		[Sub,Taxed,Total]).

calc_tax(L) -> calc_tax(L,0).
calc_tax([],Total) ->
	[Total, Total*?TAX,(1+?TAX)*Total];
calc_tax([{P,Q}|T],Total) ->
	calc_tax(T,Total+(P*Q)).
	
readItems() ->
	{ok,[P1]} = io:fread(
		"Enter the price of item 1: ","~d"),
	{ok,[Q1]} = io:fread(
		"Enter the quantity of item 1: ","~d"),
	{ok,[P2]} = io:fread(
		"Enter the price of item 2: ","~d"),
	{ok,[Q2]} = io:fread(
		"Enter the quantity of item 2: ","~d"),
	{ok,[P3]} = io:fread(
		"Enter the price of item 3: ","~d"),
	{ok,[Q3]} = io:fread(
		"Enter the quantity of item 3: ","~d"),
	if
		P1 =< 0 orelse Q1 =< 0 orelse P2 =< 0 orelse Q2 =< 0 orelse
		P3 =< 0 orelse Q3 =< 0 -> throw(negativeNumber);
		true -> ok
	end,
	[{P1,Q1},{P2,Q2},{P3,Q3}].
  • مهدی حسینی مقدم
  • ۰
  • ۰

این مثال ساده در حقیقت سعی دارد شما را با عملگرهای div و rem آشنا کند که اولی تقسیم صحیح و دومی باقیمانده است. در این مثال شما باید برنامه‌ئی بنویسید که طول و عرض یک دیوار را دریافت کند( با در نظر گرفتن این نکته که برای رنگ کردن هر 350 فوت مربع یه گالون رنگ نیاز است و همچنین امکان خرید قسمتی از یک گالون نیست و باید یک گالون کامل بخریم) و بشما اعلام کند چه تعداد گالون نیاز دارید. برای مثال اگر مساحت دیوار 351 فوت مربع باشد شما به دو گالون رنگ نیاز دارید.

-module(ex9).
-export([main/0]).
-define(GALLON, 350).

main() ->
	try run()
	catch
		error:_Error -> io:format(
			"Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.

run() ->
	{Length, Width} = readDimension(),
	D = calc_gallon(Length,Width),
	io:fwrite(
		"You will need to purchase ~w gallons of paint 
		to cover ~w square feet.~n",
		[D,Length*Width]).

calc_gallon(Length, Width) ->
	D = Length * Width,
	if
		D div ?GALLON == D / ?GALLON -> D div ?GALLON;
		true -> (D div ?GALLON) + 1
	end.
	
readDimension() ->
	{ok,[First]} = io:fread(
		"What is the length of the wall in feet? ","~d"),
	{ok,[Second]} = io:fread(
		"What is the width of the wall in feet? ","~d"),
	if
		First =< 0 orelse Second =< 0 -> throw(negativeNumber);
		true -> ok
	end,
	{First,Second}.
  • مهدی حسینی مقدم
  • ۰
  • ۰

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

Input:

How many people? 4

How many pizzas do you have? 5

How many slices each pizza have? 7

Output:

4 People with 5 pizzas which have 7 slices per each

Each Person will have 8 slices

there are 3 leftover

کد این برنامه بشرح زیر خواهد بود:

 

-module(ex8).
-export([main/0]).

main() ->
	try run()
	catch
		error:_Error -> io:format("Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.

run() ->
	{Poeple, Pizzas, Slices} = readServing(),
	io:fwrite("~w People with ~w pizzas which have ~w slices per each~n",
		[Poeple,Pizzas,Slices]),
	io:fwrite("Each Person will have ~w slices~n there are ~w leftover~n",
		[(Pizzas*Slices) div Poeple, (Pizzas*Slices) rem Poeple]).

	
readServing() ->
	{ok,[Poeple]} = io:fread(
		"How many people? ","~d"),
	{ok,[Pizzas]} = io:fread(
		"How many pizzas do you have? ","~d"),
	{ok,[Slices]} = io:fread(
		"How many slices each pizza have? ","~d"),
	if
		Poeple =< 0 orelse Pizzas =< 0 orelse Slices =< 0 -> throw(negativeNumber);
		true -> ok
	end,
	{Poeple,Pizzas,Slices}.
  • مهدی حسینی مقدم
  • ۰
  • ۰

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

-module(ex7).
-export([main/0]).
-define(CONRATE,0.09290304).

main() ->
	try run()
	catch
		error:_Error -> io:format("Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.

run() ->
	{Length, Width} = readDimension(),
	D = calc_area(Length,Width),
	io:fwrite("You entered dimensions of ~w feet by ~w feet.~n",
		[Length,Width]),
	io:fwrite("The area is~n ~w square feet~n ~w square meters~n",
		[D, D * ?CONRATE]).

calc_area(Length, Width) ->
	Length * Width.
	
readDimension() ->
	{ok,[First]} = io:fread(
		"What is the length of the room in feet? ","~d"),
	{ok,[Second]} = io:fread(
		"What is the width of the room in feet? ","~d"),
	if
		First =< 0 orelse Second =< 0 -> throw(negativeNumber);
		true -> ok
	end,
	{First,Second}.
  • مهدی حسینی مقدم
  • ۰
  • ۰

این مثال بسیار شبیه مثال قبلی است. دو عدد از ورودی در یافت می‌شود. اولی سن شماست و دومی سنی که دوست دارید بازنشسته شوید. سپس قرار است بشما بگوید که چند سال تا بازنشستگی دارید ( که خیلی ساده با کسر کردن سن کنونی از سن مورد نظر برای بازنشستگی بدست می‌آید) و چه سالی بازنشسته خواهید شد. این قسمت کمی چالشی است زیرا شما نیاز دارید با توابع خاصی سال کنونی را از سیستم استخراج کنید. تابع مورد نظر local_time است که در ماژول سیستمی calendar موجود است.  از آنجایی که ما تنها به سال کنونی نیاز داریم باقی را نادیده می گیریم. خروجی این تابع بصورت زیر است:

{{Year, Month, Day}, {Hour, Minute, Second}}
کد برنامه به شرح زیر است: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-module(ex6).
-export([main/0]).

main() ->
	try run()
	catch
		error:_Error -> io:format("Please Enter only Numeric type~n");
		throw:negativeNumber -> io:format("Please Enter only positive number~n");
		throw:outOfBound -> io:format("You are already retired~n")
	end.
	
run() ->
	{Cur,Retire} = readInteger(),
	{{Year,_,_},_} = calendar:local_time(),
	io:fwrite("you have ~w years before retirement~n",[Retire - Cur]),
	io:fwrite("It's ~w so you be retired in ~w ~n",[Year,Year+(Retire - Cur)]).

readInteger() ->
	{ok,[First]} = io:fread("What's you current age? ","~d"),
	{ok,[Second]} = io:fread("At what age would you like to retire? ","~d"),
	if
		First < 0 orelse Second < 0 -> throw(negativeNumber);
		First > Second -> throw(outOfBound);
		true -> ok
	end,
	{First,Second}.

خط 14 ما تنها مقدار سال را به متغیر Year انتقال می‌دهیم و باقی را دور می‌ریزیم. ضمنا سیستم برای مواقعی که سن مورد نظر برای بازنشستگی کمتر از سن کنونی شماس خطا خواهد داد. 

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

مثال بعدی در ظاهر بسیار ساده است. برنامه‌ئی بنویسید که دو عدد را از ورودی بگیرد و چهار عمل اصلی را با آن دو عدد انجام دهد. برای نمونه اگر بترتیی 4و 5 را وارد کنید باید خروجی مشابه زیر داشته باشیم:

4 + 5 = 9

4 – 5 = -1

4 * 5 = 20

4 / 5 = 0

 البته تقسیم در محاسبات صحیح فرض شده است. برای حل کردن این مثال مانند مثال‌های گذشته براحتی می‌توان با ورودی دریافت شده از کاربر کار کرد با این تفاوت که باید ابتدا مطمئن شویم که ورودی عدد است و نه چیز دیگر. ضمنا برای این مثال ورودی عدد منفی را نیز از کاربر نخواهیم پذیرفت. کد این برنامه بصورت زیر است:

-module(ex5).
-export([main/0]).
main() ->
	try calc()
	catch
		error:_Error -> io:format("Please Enter only Numeric type~n");
		throw:negativeNumber -> ("Please Enter only positive number~n")
	end.
		
calc() ->
	{A,B} = readInteger(),
	if
		A < 0 orelse B < 0 -> throw(negativeNumber);
		true -> ok
	end,
	[print(A,B,Op) || Op <- ["+","-","*","/"]].
		
readInteger() ->
	{ok,[First]} = io:fread("Enter the first number: ","~d"),
	{ok,[Second]} = io:fread("Enter the second number: ","~d"),
	{First,Second}.

print(A,B,Op) ->
	Res = case Op of
			"+" -> A + B;
			"*" -> A * B;
			"-" -> A - B;
			"/" -> A div B
			end,
	io:fwrite("~w ~s ~w = ~w ~n",[A, Op, B, Res]).

بطور مشخص در کد بالا سیاست ارلنگ در مواجهه با استثناءها قابل مشاهده است. سیاستی که اصطلاحا "اجازه بده خراب شود" نام دارد. به این معنی که جلوگیری از خطا و باگ ها تقریبا غیر ممکن است و بهتر است بجای آن تلاش برای کنترل شرایط پس از خطا صرف شود. در مقابل این سیاست، برنامه نویسی تدافعی defensive programming قرار دارد که سعی دارد هر خطا را در همان محل بررسی کند. برای این منظور عبارات کنترلی خطا (try/catch ) تنها در تابع آخر قرار خواهد گرفت و سایر توابع صرفا کاری برای کنترل و پیشگیری خطا انجام نخواهد داد. البته در بسیاری از زبانهای شئ‌گرا هم مدل برنامه نویسی تدافعی کنار گذاشته شده است.

در کد بالا تابع main خطا ها را بررسی می‌کند و تنها تابع calc را صدا می‌زند. این تابع سپس تابع readInteger را صدا می‌زند که وظیفه‌اش خواندن دو مقدار عددی است. و در آخر هم تابع print صدا زده می‌شود.

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

این مثال هم مانند قبلی ها بسیار ساده است، چهار مقدار ورودی می‌گیرد و یک عبارت چاپ می‌کند. برخلاف مثال قبل از trim استفاده نکردم. trim کارکترهای خالی مانند تب و اینتر را از ابتدا و انتهای ورودی حذف می‌کند که شاید خیلی دقیق نباشد زیرا ما تنها می‌خواهیم کارکتر خط جدید را از انتها حذف کنیم، برای همین از تابع دیگری استفاده کردم. نکته دیگر استفاده از export_all است که البته صرفا برای تست مناسب است.

-module(ex4).
-compile(export_all).

main() ->
	Noun = string:strip(io:get_line("Enter a noun:"),right,$\n),
	Verb = string:strip(io:get_line("Enter a verb:"),right,$\n),
	Adjective = string:strip(io:get_line("Enter an adjective:"),right,$\n),
	Adverb = string:strip(io:get_line("Enter an adverb: "),right,$\n),
	io:fwrite("Do you ~s your ~s ~s ~s? That's hilarious!~n",[Verb, Adjective, Noun, Adverb]).
  • مهدی حسینی مقدم
  • ۰
  • ۰

برنامه‌ئی بنویسید که دو مقدار رشته یکی برای نویسنده و یکی برای سخن آن نویسنده را از ورودی بگیرد و سپس آن را با فرمت زیر نمایش دهد. دقت کنید که کارکتر های خاص نظیر " نیز در رشته‌ی ورودی وجود خواهند داشت. برنامه باید مانند مثال زیر عمل کند:

What is the quote? These aren't the droids you're looking for.
Who said it? Obi-Wan Kenobi
Obi-Wan Kenobi says, "These aren't the droids you're looking for."


-module(ex3).
-export([prompt/0]).

prompt() ->
	Quote = string:trim(io:get_line("What is the quote? ")),
	Author = string:trim(io:get_line("Who said it? ")),
	io:fwrite("~s said \"~s\" ~n",[Author, Quote]).

من در حل این جواب از تابع get_line استفاده کردم. البته این تابع یک کارکتر خط جدید نیز به انتهای رشته اضافه می‌کند بنابراین مجبور به استفاده از تابع trim شدم که فضاهای خالی اول و آخر عبارت را حذف کند. همچنین به دلیل اینکه در متن نهایی باید " چاپ شود قبل از آن از کارکتر / استفاده کردم. 

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

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

%compute length of a string
-module(ex2).
-export([prompt/0]).

prompt() ->
	{ok,S} = io:fread("What is the string? ","~s"),
	io:fwrite("the length of ~s is ~w ~n",[S,string:length(S)]).
  • مهدی حسینی مقدم