Wednesday, December 27, 2006

Ruby (3)

کد بلاک ها:

کد بلاک ها یکی از ساختارهای مهم Ruby هستند. کد بلاک قطعه ای کد هست که می تواند به عنوان یک آرگومان به یک تابع فرستاده شود. بسیاری از زبان ها ساختارهایی مشابه کد بلاک دارند، در جاوا Anonymous Inner Class در سی شارپ Delegate در C و C++ اشاره گر به توابع به جای کد بلاک ها استفاده می شود (البته این ساختارها خیلی با هم فرق می کنند اما کم و بیش برای یک هدف به کار می روند) . ساده ترین کاربردی که برای استفاده از کد بلاک ها می توان تصور کرد، Action Listener ها در واسط گرافیکی کاربر است. معمولا وقتی کاربر بر روی یک دکمه کلیک می کند می خواهیم کد دلخواه ما اجرا شود.

برای درک کد بلاک ها یک مثال می تواند کمک زیادی بکند، قطعه کد زیر یک تابع به نام one_to_ten تعریف می کند که یک کد بلاک را به عنوان آرگومان می گیرد. برای ارسال یک کد بلاک به یک تابع لازم نیست که کد بلاک بصورت صریح به عنوان آرگومان تابع بیان شود(گرچه می توان بصورت صریح نیز این کار را انجام داد)، بلکه هنگامی که از کلمه کلیدی yield در تابعی استفاده کنیم، مفسر Ruby بصورت ضمنی کد بلاکی را به عنوان آخرین آرگومان تابع در نظر می گیرد. مقادیری که در جلوی yield قرار می گیرند به عنوان آرگومان به کد بلاک ارسال خواهند شد.

def one_to_ten

for i in 1..10

yield i

end

end

one_to_ten { |arg| puts arg }

# result : 1 2 3 4 5 6 7 8 9 10

فراخوانی تابع one_to_ten باید شامل کد بلاکی باشد که به آن ارسال می شود. در این مثال کد بلاک ارسال شده آرگومانی با نام arg دارد و تنها کاری که انجام می دهد چاپ مقدار آرگومان است.

کد بلاک ها در Ruby بطور گسترده ای مورد استفاده قرار می گیرند. تقریبا همه کلاس های Ruby به شکلی از کد بلاک ها استفاده می کنند. برای مثال کلاس Array (و بسیاری دیگر از کلاس ها) متدی به نام each دارند که معمولا به جای حلقه foreach در دیگر زبان ها مورد استفاده قرار می گیرد. برای مثال قطعه کد زیر مجموع اعداد موجود در آرایه a را محاسبه می کند:

a = [1, 4, 3, 2]

s = 0

a.each {|i| s = s + i }

puts s # 10

همان طور که مشاهده می کنید کد بلاک ها می توانند به متغیر های خارج از کد بلاک نیز دسترسی داشته باشند و آنها را تغییر دهند. آرایه ها در Ruby متد های جالب دیگری نیز دارند ولی در اینجا قصد ندارم آنها را توضیح دهم، فقط به عنوان یک مثال دیگر، قطعه کد زیر را در نظر بگیرید، خط اول آن حاصل ضرب عناصر آرایه a و خط دوم حاصل جمع آن ها را محاسبه می کند، خط سوم تمام عناصر آرایه را به توان 2 می رساند و در آرایه جدیدی بر می گرداند و بلاخره خط چهارم تمام عناصر آرایه را که مقدار کوچکتر یا مساوی 3 دارند حذف می کند و نتیجه را در آرایه جدیدی بر می گرداند.

puts a.inject(1) { |mult, i| i * mult } # 24

puts a.inject(0) { |sum, i| i + sum } # 10

puts a.collect { |x| x * x } # [1, 16, 9, 4]

puts a.delete_if { |i| i <= 3 } # [4]

کلاس ها و اشیاء:

همان طور که پیش از این گفتم، Ruby یک زبان کاملا شیء گرا است. قطعه کد زیر کلاسی با نام Student تعریف می کند و سپس نمونه ای از کلاس درست کرده و از آن استفاده می کند. کلاس ها در Ruby با کلمه کلیدی class و متد ها با کلمه کلیدی def تعریف می شوند. سازنده کلاس در این زبان همیشه نام initialize را دارد. در Ruby نیازی به تعریف فیلد ها درون کلاس نداریم و با قرار دادهایی محدوده(scope) متغیر ها تعیین می شود. متغیر های سراسری با $، متغیر های استاتیک با @@ و فیلدهای کلاس با @ شروع می شود. متغیرهای محلی پیشوندی ندارند و ثابت ها(constants) با حروف بزرگ شروع می شوند.

class Student

def initialize(name, age, id)

@name, @age, @uid = name, age, id

@lessons = []

end

def << (lsn)

@lessons << lsn

end

def get_name

@name

end

def to_s

"#{@name}(id:#{@uid}, age:#{@age}, lessons:[#{@lessons.join(', ')}])"

end

end

stu = Student.new('mohsen', 23, 1)

stu << 'Math'

stu << 'Data Structure'

stu << 'OS'

puts stu

در Ruby همیشه آخرین خط یک متد به عنوان مقدار بازگشتی در نظر گرفته می شود(گرچه کلمه کلیدی return هم برای بازگرداندن مقدار از متد وجود دارد). در Ruby با عملگرها دقیقا مثل متد ها رفتار می شود(برای مثال stu<<'math' دقیقا مثل فراخوانی stu.<<('math') در نظر گرفته می شود) به همین دلیل عملگرها نیز می توانند به راحتی بصورت یک متد از کلاس تعریف شوند.

کلاس های Ruby در مقابل تغییرات باز هستند به همین دلیل کاربر می تواند به کلاس هایی که قبلا نوشته و حتی کلاس های کتابخانه این زبان متد ها و فیلد هایی را اضافه کند یا آن ها را override کند(بدون اینکه نیازی به ارث بری داشته باشد). این قابلیت خیلی قدرتمندی است که در بسیاری دیگر از زبان ها وجود ندارد. برای مثال قطعه کد زیر متدی به نام to_my_string به کلاس Array که یک کلاس از کتابخانه استاندارد Ruby هست اضافه می کند.

class Array

def to_my_string

# negative indices count from the end of the string

# no return keyword needed, always last line is returned automatically

str = '{'

each{|a| str += "#{a},"}

str[-1] = '}'

str

end

end

a = ['X', '1', '2', '3']

puts a.to_my_string # {X,1,2,3}

در Ruby حتی می توان متدی به یک شیء اضافه کرد یا یکی از متد های یک شیء را override کرد! برای مثال قطعه کد زیر متدی با نام hello به متغیر a اضافه می کند.

a = 'Mohsen'

def a.hello

'hello ' + self

end

puts a.hello # hello Mohsen

این قابلیت Ruby شاید در ابتدا عجیب و یا بدون استفاده به نظر برسد ولی این ویژگی نیز قابلیت بسیار قدرتمندی محسوب می شود. برای پی بردن به این موضوع فرض کنید می خواهیم در برنامه یک connection به پایگاه داده بسازیم که قابلیت بستن نداشته باشد(یعنی فراخوانی متد close باعث بسته شدن آن نشود). این مسئله را قبلا به زبان جاوا مطرح کرده ام و پاسخ آن را هم آورده ام. در جاوا برای اینکه تنها رفتار متد close از یک شیء connection را تغییر دهیم لازم است کد بسیار زیادی نوشته شود(delegate کردن تقریبا 50 متد از کلاس Connection، گرچه این کد را eclipse به آسانی تولید می کند و زحمت زیادی برای ما ندارد، ولی در هر حال حجم زیادی از کد برای این کار لازم است). در Ruby راه حل این مسئله می تواند خیلی ساده تر باشد:

$singleConnection = create_a_connection()

def $ singleConnection.close

# do nothing !

end

$ singleConnection.close() # result : do nothing!

در مجموع ویژگی هایی که در این بخش به آن پرداخته شد(کد بلاک ها، قرار داد های نام گزاری متغیر ها، باز بودن کلاس ها و اشیاء در مقابل تغییر و ...) تا حد بسیار زیادی می تواند حجم کد نویسی را در Ruby نسبت به دیگر زبان ها کاهش دهد.

1 comment:

Anonymous said...

hi! [url=http://esnips.com/web/minnaregina/]Hello. And Bye.
[/url] http://esnips.com/web/minnaregina/ free nude video clips of milfs
thanks!
free nude video clips of milfs