روبی
مقدمه
روبی در لغت به معنای یاقوت است. هم چنین روبی نام زبانی اسکریپتی نیز هست که زبانی داینامیک، دارای reflection، عام منظوره و دارای شیگرایی است که از تلفیق syntax زبانهای perl و smalltalk به دست آمدهاست. هم چنین این زبان از Eifell و Lisp نیز تاثیر پذیرفته است. این زبان از پارادایمهای مختلفی شامل تابعی، شیگرا، دستوری و رفلکتیو پشتیبانی میکند.
نسخهی ۱.۸.۷ مفسر این زبان با زبان C پیادهسازی شدهاست. این زبان دارای dynamic type و مدیریت حافظهی خودکار است. همچنین مفسر این زبان single pass است.
تاریخچه
روبی در سال ۲۴ فوریهی سال ۱۹۹۳ توسط یوکیهیرو ماتسوموتو طراحی شد. هدف وی از طراحی این زبان جدید، برقراری تعادل میان برنامهنویسی دستوری و امری بود. ماتسوموتو میگوید:«من زبانی اسکریپتی را میخواستم که از Perl قدرتمندتر باشد و از پایتون شیگراتر. این دلیل تصمیم من است مبنی بر طراحی زبان شخصیام».
اولین release عمومی روبی در ۲۱ دسامبر ۱۹۹۵ در گروههای خبری در ژاپن اعلام شد. این release همزمان شد با راهاندازی یک mailing list برای روبی به زبان ژاپنی. در این مرحله روبی از بسیاری از امکاناتی که بسیاری از زبانها از آن برخوردارند، برخوردار بود. تعدادی از این امکانات عبارتاند از: شیگرایی، کلاسهای با وراثت، exception handling و جمعآوری زباله و ... .
روبی ۱.۰ در سال ۱۹۹۶ منتشر شد و در سال ۱۹۹۹ برای اولین بار یک mailing list به زبان انگلیسی به راه افتاد. از طرف دیگری کتابی به نام «Programming Ruby» به صورت رایگان در اختیار برنامهنویسان قرار گرفت که باعث سرعت گرفتن محبوبیت این زبان در میان انگلیسی زبانان شد.
روبی در نسخههای ۱.۲، ۱.۴، ۱.۶ و ۱.۸ به ترتیب در سالهای ۱۹۹۸، ۱۹۹۹، ۲۰۰۰ و ۲۰۰۳ منتشر شدند.
در نزدیکی سال ۲۰۰۵، RubyOnRails، یک framework برای تولید محصولات تحت وب نوشتهشد که باعث محبوبیت بیشتر روبی گردید.
در دسامبر سال ۲۰۰۷، روبی نسخهی ۱.۹ توزیع شد که تفاوتهای عمدهای با نسخههای قبلی داشت. نمونههایی از این تغییرات عبارتاند از:
- معرفی متغیرهای محلی، یعنی متغیرهایی که در بلاک تعریفشان موجودند،
- تعریف syntax جدید برای lambda
- پشتیبانی character encodingها
- واسط جدید socket
- ...
irb(main):001:0>
در صورت مشاهدهی این رشته در ترمینال میتوانید از صحت نصب روبی خود مطمئن شوید. حال اگر در اینجا بنویسید "Hello World" و سپس دکمهی Enter را فشار دهید، خواهید دید:
irb(main):001:0> "Hello World" => "Hello World"
irb(main):001:0> 2+3 => 5
irb(main):001:0> puts "Hello World" Hello World => nil
irb(main):001:0> def h irb(main):002:1> puts "Hello World!" irb(main):003:1> end => nil
شاید بهتر باشد به این مثال کمی با دقت توجه کنیم. تعریف این تابع با عبارت def h آغاز شده است که بداهتا def کلید واژهی معرف شروع تعریف تابع و h نام تابع است. در اینجا نه پرانتزی قرار گرفتهاست و نه ورودیای. پس این تابع ورودی ندارد. نکتهای که لازم به ذکر است این است که پرانتزگذاری در این زبان هم کاملا اختیاری است. تا انجایی که ابهامی رخ ندهد، میتوان از پرانتزگذاری اجتناب کرد. حال اگر نیاز به ورودی بود صرفا نام متغیرها را بعد از h قرار میدهیم.
در این مثال به نکتهی دیگری نیز توجه کنید: در promptی که روبی بعد از ورود هر دستور میدهید، دو عدد وجود دارد: مانند 002:1. در اینجا 002 شمارندهی تعداد دستورات وارد شدهاست و 1 نشاندهندهی عمق کد ماست و یا این که در چه سطحی از بلاکینگ در حال برنامهنویسی هستیم.
حال به مثال زیر توجه کنید:
irb(main):001:0> def h name="fahime" # "fahime" is name's default value irb(main):002:1> puts "Hello #{name.capitalize}!" irb(main):003:1> end => nil irb(main):004:0> h "nima" Hello Nima! => nil
دقت کنید که عبارت {name.capitalize}# در داخل علامت نقل قول قرار گرفته است. با این وجود تابع capitalize را روی name صدا میزند. این ویژگی دقیقا به همین صورت در Perl نیز وجود دارد.
کمی شیگرایی: تعریف کلاس در این زبان کاملا شبیه پایتون است. به مثال زیر توجه کنید:
irb(main):001:0> class Greeter irb(main):002:1> def initialize(name = "World") irb(main):003:2> @name = name irb(main):004:2> end irb(main):005:1> def say_hi irb(main):006:2> puts "Hi #{@name}!" irb(main):007:2> end irb(main):008:1> def say_bye irb(main):009:2> puts "Bye #{@name}, come back soon." irb(main):010:2> end irb(main):011:1> end => nil irb(main):012:0> g = Greeter.new ("Nima") => #<Greeter:0x16cac @name="Nima"> irb(main):013:0>g.say_hi Hi Nima! => nil
تا اینجا نکاتی که تقریبا از syntax روشن است را ذکر میکنیم. دقت کنید که در اینجا هیچ حرفی از تایپ متغیرهایمان نزدهایم. در حقیقت هیچگاه نیز این کار را انجام نخواهیم داد. این زبان نیز از type checking پویا تبعیت میکند. البته type checking آن strong است، زیرا هیچ تایپی را به طور ضمنی به تایپ دیگری تبدیل نمیکند. از طرف دیگر، تعریف صریح صفات کلاس در خود کلاس نیامده است. بلکه در سازنده (همان initialize) گفتهایم name@ را برابر name قرار بده. بنابراین اشیا در این زبان پویا (dynamic) هستند. این ویژگی، در پایتون نیز وجود داشت. نکتهی جالب دیگری که در این زبان وجود دارد این است که برای مشخص کردن صفات (attributeها) از یک نماد @ در ابتدای نام صفت استفاده میکنیم. این @ از حرف اول Attribute گرفته شده است. (در مورد میزان پویایی این زبان بیش از این سخن خواهیم گفت). با وجود انعطافپذیری و شاید تا حدی شلختگی این زبان، هم چنان امکان دسترسی مستقیم به صفات وجود ندارد:
irb(main):014:0>g.@name SyntaxError: compile error (irb):52: syntax error from (irb):52
irb(main):015:0> Greeter.instance_methods => ["method", "send", "object_id", "singleton_methods", "__send__", "equal?", "taint", "frozen?", "instance_variable_get", "kind_of?", "to_a", "instance_eval", "type", "protected_methods", "extend", "eql?", "display", "instance_variable_set", "hash", "is_a?", "to_s", "class", "tainted?", "private_methods", "untaint", "say_hi", "id", "inspect", "==", "===", "clone", "public_methods", "respond_to?", "freeze", "say_bye", "__id__", "=~", "methods", "nil?", "dup", "instance_variables", "instance_of?"]
irb(main):016:0> Greeter.instance_methods(false) => ["say_bye", "say_hi"]
irb(main):017:0> g.respond_to?("name") => false irb(main):018:0> g.respond_to?("say_hi") => true irb(main):019:0> g.respond_to?("to_s") => true
irb(main):020:0> class Greeter irb(main):021:1> attr_accessor :name irb(main):022:1> end => nil
توجه کنید که این تغییر اعمال شده، نه تنها در اشیای جدید، بلکه در اشیایی که از پیش ساخته شدهاند نیز اعمال خواهد شد(تکبیر)! در اینجا attr_accessor :name دو متد را به این کلاس میافزاید: یکی nameبرای گرفتن مقدار name@ و دیگری =name برای تغییر آن.
حال نوبت آن است که این زبان را به صورت غیر تعاملی نیز تجربه کنیم. با نوشتن quit یا exit میتوان از این محیط خارج شد. حال، در فایلی مانند nima.rb کد زیر را قرار دهید:
#!/usr/bin/env ruby class MegaGreeter attr_accessor :names # Create the object def initialize(names = "World") @names = names end # Say hi to everybody def say_hi if @names.nil? puts "..." elsif @names.respond_to?("each") # @names is a list of some kind, iterate! @names.each do |name| puts "Hello #{name}!" end else puts "Hello #{@names}!" end end # Say bye to everybody def say_bye if @names.nil? puts "..." elsif @names.respond_to?("join") # Join the list elements with commas puts "Goodbye #{@names.join(", ")}. Come back soon!" else puts "Goodbye #{@names}. Come back soon!" end end end if __FILE__ == $0 mg = MegaGreeter.new mg.say_hi mg.say_bye # Change name to be "Zeke" mg.names = "Zeke" mg.say_hi mg.say_bye # Change the name to an array of names mg.names = ["Albert", "Brenda", "Charles", "Dave", "Englebert"] mg.say_hi mg.say_bye # Change to nil mg.names = nil mg.say_hi mg.say_bye end
حال کافی است اجازهی اجرا شدن را به این فایل بدهید و در shell دستور nima.rb/. را اجرا کنید. دقت کنید که خط اول به shell میفهماند که باید روبی را صدا کند و آدرس این فایل را به عنوان تنها ورودی به وی بدهد. سپس روبی این فایل را باز و سپس اجرا میکند. دقت کنید که در روبی # علامتی است که مخصوص comment است. پس مشکلی در اجرای کد ایجاد نمیکند.
نظر نویسنده: شاید دلیل این که در این زبانها از # برای مشخص کردن comment استفاده میشود همین موضوع باشد. پایان نظر نویسنده!
یک نکتهی کوچک: نحوهی ایجاد مصداق: g = Greeter.new
حال باید به یک نکته در این کد بپردازیم:
@names.each do |name| puts "Hello #{name}!" end
Hello World! Goodbye World. Come back soon! Hello Zeke! Goodbye Zeke. Come back soon! Hello Albert! Hello Brenda! Hello Charles! Hello Dave! Hello Englebert! Goodbye Albert, Brenda, Charles, Dave, Englebert. Come back soon! ... ...
نکتهی دیگری که در این کد وجود دارد عبارت if __FILE__= $0 است. توجه به این نکته لازم است که شاید کسی بخواهد بعدا از این کد به عنوان کتابخانه استفاده کند. طبیعتا نباید هیچ کدی در این حالت اجرا شود. این شرط صرفا این را چک میکند که آیا این فایل، فایلی بوده که درخواست اجرای آن آمده یا خیر.
وراثت
روبی از وراثت چندگانه پشتیبانی نمیکند، اما از وراثت یگانه پشتیبانی میکند:
class A < B ....
f = File.open("testfile") begin # .. process rescue # .. handle error else puts "Congratulations-- no errors!" ensure f.close unless f.nil? end