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

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

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

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

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

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

۶ مطلب در خرداد ۱۳۹۷ ثبت شده است

  • ۰
  • ۰

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

 

int sum(int x, int y){
return x+y;
}
int twice(int x){
return 2*x;
}

ما نیاز به دو کد C دیگر نیز داریم. یک کد driver که کارش مدیریت جریان داده و پیاده سازی پروتوکل ارتباطی به ارلنگ است و در آخر هم توابع مورد نظر را صدا می‌زند. یک کد ارتباطی دیگر برای خواندن از حافظه و نوشتن به آن. دو کد به ترتیب در پایین آورده شده است:

Driver:

#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
int read_cmd(byte *buff);
int write_cmd(byte *buff, int len);
int sum(int x, int y);
int twice(int x);
int main() {
        int fn, arg1, arg2, result;
        byte buff[100];
        while (read_cmd(buff) > 0) {
                fn = buff[0];
                if (fn == 1) {
                        arg1 = buff[1];
                        arg2 = buff[2];
                        /* debug -- you can print to stderr to debug
                         * fprintf(stderr,"calling sum %i %i\n",arg1,arg2); */
                        result = sum(arg1, arg2);
                } else if (fn == 2) {
                        arg1 = buff[1];
                        result = twice(arg1);
                } else {
                        /* just exit on unknown function */
                        exit(EXIT_FAILURE);
                }
                buff[0] = result;
                write_cmd(buff, 1);
        }
}

Comm:

#include <unistd.h>                                  
typedef unsigned char byte;                          
#define HLEN 2                                       
int read_cmd(byte *buf);                             
int write_cmd(byte *buf, int len);                   
int read_exact(byte *buf, int len);                  
int write_exact(byte *buf, int len);                 
int read_cmd(byte *buf)                              
{                                                    
        int len;                                     
        if (read_exact(buf, HLEN) != HLEN)           
                return(-1);                          
        len = (buf[0] << 8) | buf[1];                
        return read_exact(buf, len);                 
}                                                    
int write_cmd(byte *buf, int len)                    
{                                                    
        byte li;                                     
        li = (len >> 8) & 0xff;                      
        write_exact(&li, 1);                         
        li = len & 0xff;                             
        write_exact(&li, 1);                         
        return write_exact(buf, len);                
}       
int read_exact(byte *buf, int len)                               
{                                                                
        int i, got=0;                                            
        do {                                                     
                if ((i = read(0, buf+got, len-got)) <= 0)        
                        return(i);                               
                got += i;                                        
        } while (got<len);                                       
        return(len);                                             
}                                                                
int write_exact(byte *buf, int len)                              
{                                                                
        int i, wrote = 0;                                        
        do {                                                     
                if ((i = write(1, buf+wrote, len-wrote)) <= 0)   
                        return (i);                              
                wrote += i;                                      
        } while (wrote<len);                                     
        return (len);                                            
}                                                                                                             

در آخر هم کد ارلنگ را نوشتم:

-module(calc).
-export([start/0, stop/0]).
-export([twice/1, sum/2]).
start() ->
        register(calc,
                 spawn(fun() ->
                                       process_flag(trap_exit, true),
                                       Port = open_port({spawn, "./calc"}, [{packet, 2}]),
                                       loop(Port)
                       end)).
stop() ->
        ?MODULE ! stop.
twice(X) -> call_port({twice, X}).
sum(X,Y) -> call_port({sum, X, Y}).
call_port(Msg) ->
        ?MODULE ! {call, self(), Msg},
        receive
                {?MODULE, Result} ->
                        Result
        end.
loop(Port) ->
        receive
                {call, Caller, Msg} ->
                        Port ! {self(), {command, encode(Msg)}},
                        receive
                                {Port, {data, Data}} ->
                                        Caller ! {?MODULE, decode(Data)}
                        end,
                        loop(Port);
                stop ->
                        Port ! {self(), close},
                        receive
                                {Port, closed} ->
                                        exit(normal)
                        end;
                {'EXIT', Port, Reason} ->
                        exit({port_terminated, Reason})
        end.
encode({sum, X, Y}) -> [1, X, Y];
encode({twice, X}) -> [2, X].
decode([Int]) -> Int.

 ابتدا برنامه C را کامپایل می‌کنیم:

gcc -o calc calc.c calc_comm.c calc_driver.c

برنامه ارلنگ را کامپایل و اجرا می‌کنیم و پس از آن براحتی می‌توانیم تابع sum را فراخوانی کنیم.

 

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

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

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

f(Args).

و روش صدا زدن خارجی، استفاده از نام ماژول مربوطه در ابتدا است:

Mod:f(Args)

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

-module(hotload).
-export([server/1, upgrade/1]).
server(State) ->
	receive
	update ->
		NewState = ?MODULE:upgrade(State),
		?MODULE:server(NewState); %% run new version
	SomeMessage ->
		do_something(),
		server(State) %% Old Version
	end.

در کد بالا کافی است پیام update را با پروسه بفرستیم و در آن صورت نسخه جدید که بصورت فراخوانی خارجی قابل مشاهده است اجرا خواهد شد. کافی است مطابق نیاز خود تابع upgrade را بنویسیم که من جایش را خالی گذاشتم.

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

در پستی که خیلی وقت ها پیش منتشر کرده بودم روش های استفاده از VIM را برای محیط ارلنگ توضیح داده بودم. در این مدت تغییراتی زیادی در این خصوص صورت گرفت و پروژه‌ی جدیدی راه اندازی شد و بنابراین تصمیم گرفتم پست جدیدی در این خصوص بنویسم.

برای راه اندازی پلاگین vim-erlang باید مراحل زیر را انجام دهیم:

 $ mkdir -p ~/.vim/bundle
 $ cd ~/.vim/bundle
 $ git clone https://github.com/vim-erlang/vim-erlang-runtime

بعد خط زیر را در فایل vimrc. اضافه کنیم(اگر فایلی به این اسم موجود نیست آن را بسازیم):

:set runtimepath^=~/.vim/bundle/vim-erlang-runtime/

مرحله‌ی بعدی نصب vim-plug است که البته برای سایر پلاگین هم قابل استفاده است، برای نصب آن باید کامندهای زیر را اجرا کنیم:

curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

حالا مجدد فایل vimrc. را باز کرده و عبارت زیر را به آن اضافه می کنیم:

call plug#begin('~/.vim/plugged')

Plug 'vim-erlang/vim-erlang-runtime'

call plug#end()

مرحله آخر اجرای برنامه vim است و پس از آن با استفاده از PlugInstall: پلاگین مربوطه را نصب می‌کنیم. کار تمام است و حالا vim برای استفاده از ارلنگ آماده است. 

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

در این مثال باید برنامه ئی بنویسیم که گذرواژه کاربر رو بررسی کند و بتواند گذرواژه های ساده، خیلی ساده، قوی و خیلی قوی را شناسایی کند. تعاریف بشرح زیر است:

گذرواژه‌ی خیلی ضعیف: متشکل از عدد است و طولی زیر 8 کارکتر دارد

گذرواژه‌ی ضعیف: متشکل از حروف است و طولی زیر 8 کارکتر دارد

گذرواژه‌ی قوی: متشکل از عدد و حروف است و طولی بیش از 8 کارکتر دارد

گذارواژه‌ی خیلی قوی: متشکل از عدد، حروف و کارکترهای ویژه نظیر !@#$%^&* است

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

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
%%@author Mahdi Hosseini Moghaddam <m.hoseini.m@gmail.com>
%%@doc Exercise 25 from "Exercises for programmers" Book
%%@reference from <a href="http://erlang.blog.ir">مثال هایی در ارلنگ</a>,
%% 2018
%%@copyright 2018 by Mahdi Hosseini Moghaddam
%%@version 0.1
-module(ex25).
-export([main/0]).
-compile([debug_info]).
-define(VERY_WEEK, {"^[0-9]{0,8}$","Very Week"}).
-define(WEEK,{"^[a-zA-Z]{0,8}$","Week"}).
-define(STRONG,{"((?=.+[0-9])(?=.+[A-Za-z])){8,}","Strong"}).
-define(VERY_STRONG,{"(?=.+[0-9])(?=.+[A-Za-z])(?=.*[!@#\$%\^&*]).{8,}","Very Strong"}).

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() ->
	{ok,Q} = readItems(),
	case passwordValidator(Q) of 
		{ok, Msg} ->
			io:fwrite("The password ~s is ~s ~n", [Q,Msg]);
		nomatch ->
			io:fwrite("No Macth ~n")
	end.
get_validation() ->
	[?VERY_STRONG, ?STRONG, ?WEEK, ?VERY_WEEK].

	passwordValidator(Str) ->
	passwordValidator(Str,get_validation()).
	
%@ this function Validates Password
passwordValidator(_Str, [] ) ->
	nomatch;
passwordValidator(Str,[{Re, Msg}|T]) ->
	case re:run(Str,Re) of
		{match, _Captured} -> {ok,Msg} ;
		nomatch -> 
		passwordValidator(Str, T)
	end.


%@doc This function reads the user input the returns the result
readItems() ->
	{ok,[P]} = io:fread(
		"Enter the password ","~s"),
	{ok,P}.

من برای بررسی سختی گذرواژه از عبارات با قاعده (Regular Expression) استفاده کردم. ماژول re ارلنگ این کار را برای من می‌کند. قسمت سخت این مثال محاسبه عبارت باقاعده برای هر سختی هست. با استفاده از ماکرو تعریف آنها را در خط 10 تا 13 مشخص کردم. 

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

در این مثال دو رشته از ورودی دریافت می‌شود و سپس بررسی می‌شود که آیا آن دو آناگرام هستند یا نه. دو رشته را در حالی آناگرام می‌گویند که از کارکترهای یکسانی تشکیل شده باشند. برای نمونه note و tone آناگرام هستند. برای حل این مثال از نوع map استفاده کردم که در آن هر کارکتر یک کلید و تعداد تکرارش مقدار آن می‌باشد.

%%@author Mahdi Hosseini Moghaddam <m.hoseini.m@gmail.com>
%%@doc Exercise 24 from "Exercises for programmers" Book
%%@reference from <a href="http://erlang.blog.ir">مثال هایی در ارلنگ</a>,
%% 2018
%%@copyright 2018 by Mahdi Hosseini Moghaddam
%%@version 0.1
-module(ex24).
-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,Q} = readItems(),
	A = isAnagram(P,Q),
	if
		A ==true -> 
			io:fwrite("~s and ~s are anagram ~n", [P,Q]);
		true ->
			io:fwrite("~s and ~s are not anagram ~n", [P,Q])
	end.
%@ this function whether two strings have the same characters
isAnagram(W1,W2) ->	
	M1 = count_char(W1),
	M2 = count_char(W2),
	maps:keys(M2) =:= maps:keys(M1).
	
count_char(Str) -> count_char(Str, #{}).
%@ this function put the characters and numbers of their occurrence in a map
count_char([H|T], X) ->
	 case maps:is_key(H,X) of
        false -> count_char(T, maps:put(H,1,X));
        true  -> Count = maps:get(H,X),
                 count_char(T, maps:update(H,Count+1,X))
    end;
count_char([], X) ->
	X.
%@doc This function reads the user input the returns the result	
readItems() ->
	{ok,[P]} = io:fread(
		"Enter the First word: ","~s"),
	{ok,[Q]} = io:fread(
		"Enter the Second word: ","~s"),
	{P,Q}.
  • مهدی حسینی مقدم
  • ۰
  • ۰

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

Enter your username: Mahdi

Enter your password:

Welcome Mahdi!

 

%%@author Mahdi Hosseini Moghaddam <m.hoseini.m@gmail.com>
%%@doc Exercise 15 from "Exercises for programmers" Book
%%@reference from <a href="http://erlang.blog.ir">مثال هایی در ارلنگ</a>,
%% 2018
%%@copyright 2018 by Mahdi Hosseini Moghaddam
%%@version 0.1
-module(ex15).
-export([main/0]).

%@doc This is my main function, It handles the exceptions and runs the program
main() ->
	try run()
	catch
		error:{badkey,_} -> io:format(
			"Your account does not exist~n");
		throw:negativeNumber -> io:format(
			"Please Enter only non-zero positive number~n")
	end.
%@doc This fucntion print out the result, it first calls readData function then check_login to calculate the result
run() ->
	Account = readData(),
	Result = check_login(Account),
	if
		Result == true ->
			io:fwrite("Login Succeed! ~n");
		true ->
			io:fwrite("Login failed! ~n")
	end.
%@doc This function check the account exist it first try to retrieve the account, if the account exist it then compare the hash version of the entered password with the stored one. if the account does not exist it prompt different message 
check_login({User, Passwd}) ->
	Map = accounts(),
	Val=maps:get(User, Map),
	Val == crypto:hash(sha256,Passwd).
accounts() ->
		#{"mahdi" => crypto:hash(sha256,"123456")}.
%@doc This function reads the user input the returns the result
readData() ->
	Usr = string:strip(io:get_line("Enter your username: "),right,$\n),
	io:fwrite("Enter you password:"),
	Passwd = io:get_password(),
	{Usr, Passwd}.
  • مهدی حسینی مقدم