تبدیل ارز

تبدیل ارز

تمرین یادگیری

مقدمه

Julia یک زبان همه‌منظوره است که می‌توان از آن برای بیشتر کارهای برنامه‌نویسی استفاده کرد. اما در عمل، کاربردهای اصلی آن بیشتر در مهندسی و علوم است. محاسبات عددی سریع، انعطاف‌پذیر و پیشرفته، نقش محوری در طراحی آن دارند.

اعداد صحیح

عدد صحیح یک عدد «گرد» است که نقطه‌ی اعشار ندارد.

در مفهوم مبانی دیدیم که می‌توان یک مقدار صحیح را بدون تعیین نوع به یک متغیر اختصاص داد.

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

julia> x = 3
3

julia> typeof(x)
Int64

julia> large_number = 1_234_567_890
1234567890

اعداد اعشاری

تعجبی ندارد که اعداد اعشاری یک نقطه‌ی اعشار و بخشی کسری پس از آن داشته باشند.

julia> f = 3.45
3.45

julia> typeof(f)
Float64

البته نماد علمی هم پشتیبانی می‌شود.

julia> avogadro = 6.02e23
6.02e23

شاید بیشینه و کمینه‌ی مقادیر غافلگیرکننده باشند:

julia> typemax(Float64)
Inf

julia> typemin(Float64)
-Inf

بینهایت یک مقدار معتبر است!

عملگرهای حسابی

همان‌طور که در مفهوم مبانی بحث شد، عملگرهای حسابی تقریباً مانند حساب معمولی عمل می‌کنند، همان حسابی که در مدرسه به کودکان آموزش داده می‌شود. توجه کنید که توان از ^ استفاده می‌کند، نه ** (هر دو در زبان‌های دیگر رایج‌اند).

2 + 3  # 5 (addition)
2 - 3  # -1 (subtraction)
2 * 3  # 6 (multiplication)
8 / 2  # 4.0 (division)
8 % 3  # 2 (remainder)
2 ^ 3  # 8 (exponentiation)

با این حال، چند نکته‌ی خاص Julia هست که ارزش پرداختن دارد.

ضرب

julia> x = 4.2
4.2

julia> 2 * x
8.4

julia> 2x
8.4

julia> 2.4x
10.08

این ممکن است غافلگیرکننده باشد.

همیشه می‌توان از * به عنوان عملگر میان‌وندی استفاده کرد، مانند بیشتر زبان‌های برنامه‌نویسی دیگر.

اما Julia را کسانی طراحی کرده‌اند که باور دارند کد باید تا حد امکان شبیه معادلات ریاضی به نظر برسد.

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

برای مثال، اگر مساحت سطح یک کره را بخواهیم، به جای 4 * pi * r * r می‌توانیم این کار را بکنیم:

julia> surface(r) = 4π * r^2
surface (generic function with 1 method)

julia> surface(3)
113.09733552923255

هرچند π یک ثابت از پیش تعریف‌شده است، یک حرف (یونانی) هم هست. بنابراین تجزیه‌گر هنوز به یک * صریح نیاز دارد تا π را از r جدا کند.

تقسیم

استفاده از / به عنوان عملگر میان‌وندی همیشه یک نتیجه‌ی اعشاری می‌دهد، حتی برای ورودی‌های صحیح.

برای تقسیم صحیح، گزینه‌های بیشتری وجود دارد.

julia> 10 / 3  # floating-point division
3.3333333333333335

julia> div(10, 3)  # integer division
3

julia> 10 ÷ 3  # synonym for div()
3

julia> 10 // 3  # rational number (fraction)
10//3

تابع div() برای تقسیم صحیح است و نتیجه‌اش به سمت صفر بریده می‌شود: برای اعداد مثبت به پایین و برای اعداد منفی به بالا.

به عنوان مترادف، می‌توانیم از عملگر میان‌وندی ÷ استفاده کنیم، باز هم با این هدف که ریاضی‌تر به نظر برسد. اگر از ویرایشگری استفاده می‌کنید که Julia را می‌شناسد، آن را به صورت \div وارد کنید و بعد کلید <Tab> را بزنید.

عملگر // فراتر از حوزه‌ی این مفهوم است. فعلاً می‌توانیم فقط بگوییم که نتیجه‌ی // یک عدد «گویا» است، همان چیزی که بیشتر مردم کسر می‌نامند.

تبدیل انواع عددی

این کار اغلب می‌تواند خودکار انجام شود:

julia> x = 2 + 3.5
5.5

julia> typeof(x)
Float64

یک Int64 را به یک Float64 اضافه کردیم و نتیجه‌ی Float64 گرفتیم.

در واقع، عدد صحیح بی‌صدا به Float64 تبدیل شد و بعد جمع انجام گرفت.

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

  • تابع round() به نزدیک‌ترین عدد صحیح گرد می‌کند؛ وقتی عددی مانند ۴.۵ دقیقاً وسط دو عدد صحیح باشد، به نزدیک‌ترین عدد صحیح زوج گرد می‌شود.
  • floor() به پایین گرد می‌کند، ceil() به بالا و trunc() به سمت صفر.
  • تلاش برای تبدیل مستقیم نوع، مثلاً با Int32()، با خطای InexactError شکست می‌خورد.

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

julia> round(4.5)
4.0

julia> ceil(Int, 4.3)
5

گرد کردن به تعداد مشخصی رقم پس از نقطه‌ی اعشار هم با کلیدواژه‌ی digits امکان‌پذیر است.

julia> round(π, digits=10)
3.1415926536

تقسیم بر صفر

حتماً این فقط یک خطا ایجاد می‌کند؟ در واقع، ماجرا آن‌قدرها هم ساده نیست.

تقسیم صحیح با ÷ یا // همان‌طور که انتظار دارید به خطا می‌انجامد.

تقسیم اعشاری با / رویکردی را در پیش می‌گیرد که می‌توان آن را رویکردی مهندسی دانست، نه رویکرد استاندارد علوم کامپیوتر:

julia> 2 / 0
Inf

julia> 0 / 0
NaN

همان‌طور که در بخش قبلی بحث شد، بینهایت در Julia یک عدد اعشاری معتبر است که با Inf نشان داده می‌شود.

وقتی صورت کسر هم صفر باشد، نتیجه از نظر ریاضی تعریف‌نشده است. در این حالت Julia آن را «عدد نیست» در نظر می‌گیرد که با NaN نشان داده می‌شود.

دستورالعمل‌ها

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

1. تخمین ارزش پس از تبدیل

تابع exchange_money() را بسازید که ۲ پارامتر می‌گیرد:

  1. budget : مبلغی که قصد دارید تبدیل کنید.
  2. exchange_rate : مقدار ارز داخلی که برابر با یک واحد ارز خارجی است.

این تابع باید ارزش ارز تبدیل‌شده را برگرداند.

توجه: اگر ارز شما USD است و می‌خواهید USD را با نرخ تبدیل 1.20 به EUR تبدیل کنید، آنگاه 1.20 USD == 1 EUR.

julia> exchange_money(127.5, 1.2)
106.25

2. محاسبه‌ی ارز باقی‌مانده پس از تبدیل

تابع get_change() را بسازید که ۲ پارامتر می‌گیرد:

  1. budget : مبلغ پول پیش از تبدیل.
  2. exchanging_value : مبلغی که از بودجه برداشت می‌شود تا تبدیل شود.

این تابع باید مبلغی را برگرداند که از بودجه باقی می‌ماند.

julia> get_change(127.5, 120)
7.5

3. محاسبه‌ی ارزش اسکناس‌ها

تابع get_value_of_bills() را بسازید که ۲ پارامتر می‌گیرد:

  1. denomination : ارزش یک اسکناس.
  2. number_of_bills : تعداد کل اسکناس‌ها.

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

julia> get_value_of_bills(5, 128)
640

4. محاسبه‌ی تعداد اسکناس‌ها

تابع get_number_of_bills() را بسازید که amount و denomination را می‌گیرد.

این تابع باید تعداد اسکناس‌های ارز را برگرداند که می‌توانید در محدوده‌ی amount داده‌شده دریافت کنید. به بیان دیگر: چند اسکناس کامل در مبلغ اولیه جا می‌گیرد؟ یادتان باشد که فقط می‌توانید اسکناس کامل دریافت کنید، نه کسری از اسکناس، پس تقسیم را بر همین اساس انجام دهید. در واقع دارید به نزدیک‌ترین اسکناس/واحد کامل به پایین گرد می‌کنید.

julia> get_number_of_bills(127.5, 5)
25

5. محاسبه‌ی باقی‌مانده پس از تبدیل به اسکناس

تابع get_leftover_of_bills() را بسازید که amount و denomination را می‌گیرد.

این تابع باید مبلغ باقی‌مانده را برگرداند؛ مبلغی که با توجه به واحد اسکناس‌ها نمی‌توان از amount اولیه‌ی شما پس داد. دانستن اینکه باجه دقیقاً چقدر را برای خودش نگه می‌دارد بسیار مهم است.

julia> get_leftover_of_bills(127.5, 20)
7.5

6. محاسبه‌ی ارزش پس از تبدیل

تابع exchangeable_value() را بسازید که budget، exchange_rate، spread و denomination را می‌گیرد.

پارامتر spread درصدی است که به عنوان کارمزد تبدیل گرفته می‌شود و به صورت یک عدد صحیح نوشته شده است. باید آن را با تقسیم بر ۱۰۰ به عدد اعشاری تبدیل کنید. اگر 1.00 EUR == 1.20 USD باشد و spread برابر 10، نرخ تبدیل کل در واقع چنین خواهد بود: 1.00 EUR == 1.32 USD، چون ۱۰٪ از ۱٫۲۰ برابر ۰٫۱۲ است و این کارمزد اضافه به تبدیل افزوده می‌شود.

این تابع باید بیشترین ارزش ارز جدید را پس از محاسبه‌ی نرخ تبدیل به‌علاوه‌ی spread برگرداند. یادتان باشد که واحد ارز یک عدد کامل است و نمی‌تواند تقسیم شود.

توجه: مقدار بازگشتی باید از نوع عدد صحیح باشد.

julia> exchangeable_value(127.25, 1.20, 10, 20)
80

julia> exchangeable_value(127.25, 1.20, 10, 5)
95
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Julia Exercism

آماده‌اید تبدیل ارز را شروع کنید؟

در Exercism ثبت‌نام کنید تا Julia را همراه با 35 مفهوم128 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.