Skip to main content

احتمالا این روزها با داغ بودن بحث بلاک‌چین و بیت‌کوین و سایر مباحث مرتبط با رمزارزها، احتمالا اگر عمیق‌تر از سایرین وارد ماجرای مرتبط با رمزارز شده باشید، متوجه شدید که یکی از مهم‌ترین بخش‌های تعاریف مرتبط با رمزارز، الگوریتمی به نام «اثبات کار» یا Proof of work است.

در واقع، اگر بخواهیم با دید زندگی واقعی به این الگوریتم نگاه کنیم به این شکل نگاه خواهیم کرد :

یک معلم، تا وقتی شما پاسخ سوال رو به درستی ننوشته باشید نمره‌ای به شما نمیده.

در واقع، شما وقتی پاسختون رو روی برگه امتحانی می‌نویسید، دارید «کار» انجام می‌دید و اگر جواب شما «درست» باشه، «پاداش» می‌گیرید. کار شما اینجا فعالیت فکری و نوشتن پاسخ درست سوال و پاداش شما، نمره (و احتمالا امکان اخذ درس بعدی) است.

در دنیای رمزارزها هم در واقع ما منابع پردازشی خودمون رو در اختیار یک سیستم بزرگتر میذاریم و در ازای کاری که داریم انجام میدیم پاداش می‌گیریم (که این پاداش، میتونه بیت‌کوین باشه).

اثبات کار در رمزارز چطور کار می‌کنه؟

در رمزارزهای مختلف معمولا روش های متفاوت و مختلفی برای اثبات کار استفاده میشه. اما اجازه بدید کمی بهتر در این باره صحبت کنیم.

یک مثال خیلی ساده رو در نظر بگیریم :

رشته های مختلفی در یک سرور وجود دارند که به صورت متن خالص (plain text) نوشته شدند. ما میخواهیم شما این ها رو طوری hash کنید که پنج رقم ابتدای هش، حتما 1a1b0 باشه.

اگر نمی‌دونید هش چیه، ویدئوهای جادی (قسمت اول، قسمت دوم، قسمت سوم) می‌تونه در درک هش به شما کمک کنه.

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

اصلا پیچیده نیست، اما این که دقیقا چه میشه کرد که این اتفاق بیفته، راه‌های مختلفی داره. ساده ترین راه حلش رو قراره با هم در ادامه پیاده کنیم.

پیاده سازی در روبی

هش کردن در روبی

برای هش کردن داده‌ها در روبی، از کتابخانه digest استفاده می‌کنیم. برای استفاده از این کتابخانه، شما نیازی ندارید تا جم یا افزونه خاصی نصب کنید. چرا که همراه خود روبی نصب میشه. فقط کافیه در کدتون این کار رو کنید :

require 'digest'

و سپس برای دریافت هش مورد نظر (در این مطلب از sha256 استفاده خواهیم کرد) کافیه که چنین کاری کنیم :

hash = Digest::SHA256.hexdigest("Hello, world!")
 
puts hash

و پاسخ ما چنین خواهد بود :

315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3

و اما بریم سراغ همون ساده ترین راه حلی که داشتیم، این راه حل بعنوان یک راه حل ساده، به ذهن من رسید. راه حل اینه :

یک عدد در نظر بگیر، اون رو به یک رشته متنی تبدیل کن، اون رو بچسبون به رشته‌ای که روی سرور بوده. تا وقتی هم که پنج رقم اول هش، اونی که من میگم نشده، این کار رو ادامه بده.

خب کد ما در نهایت چنین چیزی میشه :

########
# POW in Ruby 
########
 
require 'digest'
 
i = 0 
while true
    i = i + 1 
    string = "Sample Text" + i.to_s 
    hash = Digest::SHA256.hexdigest(string)
 
    if hash[0..4] == "1a1b0" 
 
        puts "--------"
        puts "Work proven. You worked so hard :)"
        puts "The result is #{string} and the hash is #{hash}"
        break 
 
    else 
        puts "We are at attempt No. #{i}"
    end  
 
end

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

We are at attempt No. 751270
We are at attempt No. 751271
We are at attempt No. 751272
--------
Work proven. You worked so hard :)
The result is Sample Text751273 and the hash is 1a1b0ae39e178300c5ae7650d9c809da499eb165f7e621b70c0634d673874b5d

همونطور که می‌بینید بعد از ۷۵۱۲۷۳ تلاش، تونسته اون چیزی که ما مد نظر داریم رو برامون بسازه.

این تمرین خوبیه. ولی برای کار واقعی خوب نیست. چرا؟

چیزی که پیاده شده، علیرغم این که کار میکنه و خوبه؛ از نظر پیچیدگی زمانی مشکل داره. در واقع، تمام این پیچیدگی باعث شده شدیدا به سخت‌افزار خوب و سریع؛ وابسته باشه. در صورتی که معمولا الگوریتم‌های درست و حسابی هرقدر هم لاینحل باشن، به همین سادگی‌ها نباید استقلالشون از سخت افزار رو از دست بدن. نتیجتا علیرغم این که یک طرح مفهومی خوب برای نشون دادن POW محسوب میشه، اصلا برای اجرا روی پروژه‌های جدی که نیاز به اثبات کار دارند، مناسب نیست.

در پایان هم امیدوارم که این آموزش، به دردتون خورده باشه. راستی شما برای اثبات کار چه می‌کنید؟ در بخش نظرات منتظرتونیم 🙂

در سال ۱۳۹۳ بود که نسخه اول کتاب آموزش روبی نگاشته و منتشر شد، اکنون، در روز ۲۸ شهریور ماه ۱۳۹۵ بعد از گذشت حدود دو سال، دومین نسخه از این کتاب عرضه می شود.

نسخه جدید کتاب علاوه بر این که با فرمت جدیدی نگاشته شده است، به صورت آنلاین (از آدرس : http://book.rubydev.ir ) نیز در دسترس است. همچنین سرفصلهای کتاب تغییر کرده و شکل و شمایل جدیدی به خود گرفته است. سرفصل های کتاب به این شکل هستند :

  • مقدمه
  • آشنایی با روبی
  • پایه ها و مقدمات
  • متغیر ها و انواع داده ها
  • آرایه ها و جداول درهم سازی
  • عملگرها
  • ساختارهای کنترلی
  • حلقه های تکرار
  • توابع
  • برنامه نویسی پیشرفته در روبی
  • برنامه نویسی شی گرا در روبی
  • برنامه نویسی کاربردی در روبی
  • یک پروژه نمونه

rubybookcover

روند کار کتاب جدید، به این شکل است که ابتدا از سیر تا پیاز روبی را به مخاطب آموزش میدهد، سپس یک پروژه با استفاده از چارچوب قدرتمند «سیناترا» اجرا میکند تا کاربر بتواند هرآنچه آموخته است را به کار بگیرد.

لینک دریافت کتاب

 

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

الگوی کلی عبارات با قاعده در روبی، به شکل زیر است :

/[0-9a-zA-Z]/ #برای مجموعه ای از کرکترها
/hay/ #برای چک کردن یک قسمت خاص در یک رشته و تطبیق کلی
/\(\)/ #برای چک کردن نمادها

برای چک کردن برابری مقادیر یک رشته با الگوی خاص در روبی از === (سه علامت مساوی) استفاده میکنیم. بعنوان مثال، برنامه زیر به ما میگوید که مقدار وارد شده یک ایمیل است یا خیر :

/[0-9a-zA-Z]\@[0-9a-zA-Z]\.[a-z] === "a@gmail.com"

که این قطعه کد، مقدار true را برمیگرداند. چرا که الگوی کلی یک آدرس ایمیل را داده ایم . برای بهینه تر شدن، میتوانیم یک قطعه کد بنویسیم که چک کند کاربر از چه سرویس دهنده ای استفاده میکند.

if /[0-9a-zA-Z]\@gmail\.com/ === user.email
 puts "User #{user.username} uses Gmail"
end
if /[0-9a-zA-Z]\@yahoo\.com/ === user.email
 puts "User #{user.username} uses Yahoo!"
end

همچنین با استفاده از متد match میتوان چک کرد که آیا بخشی از عبارت ورودی، برابر الگوی وارد شده هست یا نه. برای مثال برنامه بالا را میتوانیم به این شکل بازنویسی کنیم :

if /\@gmail\.com/.match(user.email)
 puts "User #{user.username} uses Gmail"
end
if /\@yahoo\.com/.match(user.email)
 puts "User #{user.username} uses Yahoo!"
end

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

/\([\+\-\*\/][0-9][0-9]\)/ === command

و با استفاده از این اساس، میتوان مبادرت به نوشتن Parser های مختلف نمود.

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

 

در آموزش پیشین ، در مورد تبدیل نوع کلاس ها و همچنین ارث بری به قدر کافی صحبت شد. در این آموزش، قصد داریم تا در مورد «سر بارگذاری عملگرها» یا Operator Overloading بحث کنیم. وقتی یک کلاس جدید ایجاد میکنیم، به طور مستقیم نمی توانیم روی متغیرهای instance از عملگرها استفاده کنیم. به همین خاطر، مجبوریم که خودمان دوباره اپراتور ها را بازنویسی کنیم.

برای درک بهتر موضوع، وقتی یک عدد مانند ۱ را داخل یک متغیر قرار میدهید، یک شی از کلاس Fixnum ایجاد شده و این شی قابلیت این را دارد که با شی دیگری از کلاس Fixnum جمع بسته شود، از آن کم شود و … . بنابراین، نیاز به این داریم که تعریف کنیم عملگری مانند + وجود دارد که با استفاده از آن، میتوان عدد دیگری اضافه نمود. بیایید یک کلاس به نام TestNumber ایجاد کنیم و عملگر + را برایش تعریف کنیم.

class TestNumber
 def initialize(x)
  @x = x
 end
 def +(y)
  @x + y
 end
end

اگر دو شی از این کلاس ایجاد کنید و بخواهید روی آنها عملیات انجام دهید، حتما به مشکل میخورید. چرا که ما تعریف کردیم که متغیر x با y جمع شود و نه yی که در یک کلاس دیگر تعریف شده است.

برای این که x ما با yی که در یک کلاس دیگر تعریف شده است جمع بسته شود نیاز داریم تا متغیرهای instance آن کلاس را هم فراخوانی کنیم. پس کد ما به این شکل تغییر خواهد کرد :

class TestNumber
 attr_accessor :x
 def initialize(x)
  @x = x
 end
 def +(other)
  y = other.x
  TestNumber.new(@x + y)
 end
end

با استفاده از attr_accessor میتوانیم به متغیرهای instance دیگر کلاس ها دسترسی مستقیم داشته باشیم و مانند یک متد آنها را فراخوانی کنیم.

در این آموزش، مبحث Operator Overloading را پوشش دادیم. در آموزش های بعدی، به سایر مفاهیم و تکنیک های برنامه نویسی شی گرا در روبی خواهیم پرداخت.

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

class User
 def initialize(username, password)
  @username = username
  @password = password
 end
 def print_info
  puts "username: #{@username}"
  puts "password: #{@password.length * "*"}"
 end
end

بیایید یک برنامه بزرگ را در نظر بگیریم، مثلا قرار است از کاربرها، عده ای را به عنوان ادمین انتخاب کنیم، و عده ای را به عنوان ویرایشگر. باید چه کنیم؟ اولین راهی که به ذهن برنامه نویس تازه کار میرسد، نوشتن کلاس ادمین و ویرایشگر از صفر است. اما بیاید از ویژگی «ارث بری» یا Inheritance در روبی بهره ببریم.

نوشتن کلاس ویرایشگر و ادمین

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

class Editor < User
end

باید مجوز های خاصی را به کلاس ویرایشگر اضافه کنیم. مثلا بگوییم میتواند ویرایش کند یا نه؟ میتواند کاربری را اضافه و حذف کند و … ! بیاییم با متد permissions مجوز ها را مشخص کنیم :

class Editor < User
 def permissions
  puts "Can Edit, Add and Remove Articles"
 end
end

و به همین ترتیب، کلاس ادمین را هم میسازیم :

class Admin < User
 def permissions
  puts "Can Edit, Add, Remove all Articles and Users"
 end
end

بدین شکل، کاربر ادمین هم ساختیم. حال سوال پیش می آید چگونه میتوان کلاس ها را به یکدیگر تبدیل نمود؟ مثلا کاربر عادی را به ادمین، ادیتور و ادمین را به یکدیگر و … ؟ در ادامه با ما باشید.

تبدیل نوع کلاس ها

ما اکنون سه کلاس Admin و Editor و User را داریم. برای این که این کلاس ها را به یکدیگر تبدیل کنیم باید چه کنیم؟ دقت کنید که نحوه تبدیل به این شکل است که کاربر، به ویرایشگر و ادمین؛ ادمین به کاربر و ویرایشگر ؛ و ویرایشگر به کاربر و ادمین تبدیل می شود. در واقع تبدیل نوع کلاس باید به این شکل باشد که کلاس جدیدی از نوع مورد نظر ما ایجاد شده، و اطلاعات آن منتقل شود. نمونه کد زیر، تبدیل کننده کاربر به ویرایشگر و ادمین است :

class User
 def initialize(username, password)
  @username = username
  @password = password
 end
 def print_info
  puts "username: #{@username}"
  puts "password: #{@password.length * "*"}"
 end
 def to_editor
  Editor.new(@username, @password)
 end
 def to_admin
  Admin.new(@username, @password)
 end
end

و به همین ترتیب، می توانید این متدها را برای دو نوع کاربر دیگر هم بنویسید ( که به علت طولانی شدن مطلب، از نوشتن آنها صرف نظر کردیم).

تا اینجا، با دو تا از مهم ترین تکنیک های شی گرایی در روبی آشنا شدیم. در آینده، سایر روش ها و تکنیک های برنامه نویسی شی گرا در روبی را نیز فراخواهیم گرفت.

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

ساختار کلی کلاس ها در روبی

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

class Name
end

توجه داشته باشید که نام کلاس ها، حتما باید مانند ثوابت با حرف بزرگ انگلیسی شروع شود. مثلا اگر کلاس شما، کلاس Animal است، نمی توانید آن را به شکل animal نام گذاری کنید. در فاصله بین نام کلاس تا end ، متدهای یک کلاس را قرار میدهیم. مهم ترین متد هر کلاس، متد initialize است، که کلاس را مقداردهی اولیه میکند. برای مثال کلاس User را با دو مقدار اولیه username و password در نظر بگیرید :

class User
 def initialize(username, password)
  @username = username
  @password = password
 end
end

متغیرهایی که با @ شروع می شوند، متغیر های instance هستند، بدون دادن مقدار کافی به این متغیرها، کلاس(های) شما، ایجاد نمی شوند. به این شکل، یک شی از کلاس User ایجاد می کنیم :

user = User.new("username", 123456)

با استفاده از متد new ، مقدار دهی اولیه را انجام میدهیم. اما هنوز متدی در نظر نگرفته ایم که با استفاده از آن، بتوانیم یوزرنیم کاربر را دریافت کنیم، و پسورد کاربر را به شکل **** نشان دهیم، برای این کار، متد print_info را در کلاس User تعریف میکنیم.

class User
 def initialize(username, password)
  @username = username
  @password = password
 end
 def print_info
  puts "username: #{@username}"
  puts "password: #{@password.length * "*"}
 end
end

اکنون، متد مورد نظر ما قابلیت اجرایی شدن را داراست. پس با استفاده از print_info نتیجه مطلوب را دریافت خواهیم کرد.

توجه داشته باشید که این مقاله، آموزش ابتدایی و پایه‌ای برنامه سازی پیشرفته در روبی بود، در مقالات آینده، از امکانات بیشتری استفاده خواهیم نمود.

مدتی بود که داشتم در این تاپیک در فروم اوبونتو برای آموزش زبان روبی رو مینوشتم. این زبان، زبانی بود که مدت نسبتا زیادی بهش علاقه داشتم و خب شکر خدا یادش هم گرفتم. حالا، کتاب کارش تمام شده. کتاب حدود ۵۴ صفحه داره و در قالب یک PDF نوشته شده. تا حد امکان سعی کردم ساده توضیح بدم که تازه کارها هم بتونن به خوبی این زبان رو یاد بگیرن.
سر فصل های کتاب :

 

  • مقدمه
  • پایه ها و مقدمات روبی
  • انواع داده ها و متغیرها، ثابت ها و عملگرها
  • آرایه ها و هش ها
  • ساختارهای کنترلی
  • حلقه های تکرار
  • توابع
  • کلاس ها و شی گرایی

 

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