رفتن به مطلب
جامعهٔ برنامه‌نویسان مُدرن ایران

تمامی فعالیت ها

این جریان به طور خودکار بروزرسانی می شود     

  1. امروز
  2. با سلام و درود، نسخهٔ ۵.۱۴.۰ به عنوان یک نسخهٔ ما قبل نهایی از ۵.۱۵ همراه با ویژگی‌های بسیاری ارائه شده است که در این میان ویرایش ۵.۱۴.۲ بعد از ۵.۱۴.۱ با برخی از بهبود‌ها و حل مشکلات گزارش شده منتشر شد. برخی از ویژگی‌های بسیار کاربردی که به کمک دوستان عزیز، @سروش ربیعی و @hamed_masafi به کتابخانهٔ QCalendar افزوده شده است. و از این نسخه به بعد امکان استفاده از تاریخ شمسی در کیوت فراهم می‌شود. شما می‌توانید این نسخه از کتابخانه را در این بخش دریافت کنید. یک مثال ساده: #include <QCoreApplication> #include <QCalendar> #include <QDate> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QCalendar cal(QLatin1String("jalali")); qDebug() << "availableCalendars=" << QCalendar::availableCalendars(); QString todayJalali = QDate::currentDate().toString("yyyy-MM-dd", cal); qDebug() << "Today in jalali calendar=" << todayJalali; QDate d(1398, 10, 15, cal); qDebug() << "Date 1398/10/15 in julian calendar=" << d.toString(); return a.exec(); } ویژگی‌های جدید به صورت زیر می‌باشند: ماژول Qt3d Threading architecture overhaul (removal of aspect thread) Frontend/Backend node sync overhaul QTransform now has access to the world matrix Introduction of Scene3DView Scene3D is now in sync with QtQuick Scene3D can now render as an underlay without FBO ماژول Qt Bluetooth [QTBUG-40698] Optional win32 backend to support Windows 7 and 8. Backend is not built by default and not part of the prebuild packages though. QLowEnergyController: Introduce AuthorizationError [QTBUG-76615] Ability to have multiple manufacturer data per manufacturer ID ماژول QtCore QCalendar adds support for Calendars other than Gregorian, by implementation of suitable back-ends. Back-ends to implement the Gregorian, Jalali (Persian), Islamic Civil, Milankovic, and Julian calendars. We look forward to other contributions. [QTBUG-14150] Qt is now relocatable, i.e. it's possible to move the Qt installation without breaking functionality or loading of plugins. ماژول Qt GUI QColorConstants provide constexpr QColor instances that don't cost any runtime overhead Updated High-DPI support. Applications can now opt-in to use non-integer scale factors, for example Windows at 150%. Use QGuiApplication::highDpiScaleFactorRoundingPolicy or QT_SCALE_FACTOR_ROUNDING_POLICY to set the rounding policy. Added the QT_ENABLE_HIGHDPI_SCALING environment variable which enables high-dpi scaling based on display DPI. Replaces QT_AUTO_SCREEN_SCALE_FACTOR (now deprecated), and corresponds to the Qt::AA_EnableHighDpiScaling application attribute. The QT_FONT_DPI environment variable is now supported cross-platform, for the purpose of developing and testing with specific DPI values. Color-space support for images. Reading and writing color-spaces from JPEG, PNG, WebP and TIFF images, and performing color-space transformation on images. QTextDocument/QTextTable now support per-edge border styling via QTextTableCellFormat border-collapse mode HTML table style import (partial) and export QTextDocument supports reading and writing Markdown format, as an alternative to HTML. If you read HTML and write Markdown, or vice-versa, the formatting should be preserved to the extent that the CommonMark and GitHub specs allow (including headings, tables, bullet lists, block quotes and code blocks); but we don't guarantee all cases yet, because it's thinly tested so far. ماژول Qt Multimedia Added QVideoFrame::Format_YUV422P. Introduced support of GStreamer OpenGL plugin. ماژول Qt Network HTTP/2 configuration API Network connectivity monitoring [QTBUG-1538] Support Kerberos Proxy Authentication ماژول Qt QML Added qmlRegisterSingletonInstance function. This allows to expose a QObject as a singleton to QML, without having to create a factory function as required by qmlRegisterSingletonType. It is meant as a type safe replacement of setContextProperty in common usages. Added qmlRegisterAnonymousType as a replacement for qmlRegisterType. It allows to specify the URI and major version, which enables better tooling support. qmllint gained an experimental -U option. If run with it, it warns about accesses to unqualified identifiers ماژول Qt Quick Added the first preview of the graphics API independent scenegraph renderer as an opt-in feature. This allows running qualifying Qt Quick applications on top of Vulkan, Metal, or Direct3D 11 instead of OpenGL. The supported platforms are currently Windows 10, Linux with X11 (xcb), macOS with MoltenVK, or Android 7.0+ for Vulkan, macOS for Metal, Windows 10 for D3D. Text and TextEdit now support Markdown format (CommonMark and GitHub dialects) as an alternative to HTML. Includes the GitHub checklist extension, such that you can click to toggle checkboxes in a TextEdit. TextEdit uses an I-beam cursor by default, and a pointing-hand cursor when hovering a checkbox or a link. You can still override the default cursor, though. Added WheelHandler, an Event Handler for the mouse wheel, and optionally for emulated mouse wheel events coming from a trackpad. Added BoundaryRule in Qt.labs.animation: a PropertyValueInterceptor that restricts the range of values a numeric property can have, applies "resistance" when the value is overshooting, and provides the ability to animate it back within range. It's particularly useful in combination with WheelHandler, to provide similar physics as Flickable has. Event Handler base classes such as QQuickSinglePointHandler and QQuickMultiPointHandler have private implementation, while the classes themselves are suitable for subclassing, and are exported. They do not have supported public C++ API yet, but we encourage you to experiment with subclassing them anyway using the private API. They are intended to become public at some point in Qt 6 (and we can continue to iterate the API until then, especially to keep up with upcoming changes to the QEvent classes in Qt 6). Image and BorderImage now have the same currentFrame and frameCount properties that AnimatedImage has; this allows choosing an individual ic ماژول Qt SerialBus [QTBUG-72979] Added operators to compare QCanBusDevice::Filter for equality or inequality. [QTBUG-70449] Added the QCanBusDevice::OperationError and QCanBusDevice::TimeoutError codes to signal wrong operation respectively timeout errors. [QTBUG-70766] Added the function QCanBusDevice::busStatus() to query the CAN bus status from the CAN bus device. [QTBUG-54943] Added the function QCanBusDevice::resetController() to reset a CAN controller from bus off state. [QTBUG-75204] SocketCAN: Added the configuration parameter QCanBusDevice::ProtocolKey to use another protocol inside the protocol family PF_CAN. [QTBUG-54296] SocketCAN: If libsocketcan is available, the CAN bus bitrate can be get and set at runtime. PeakCAN: Added support for PCAN-USB devices on macOS by using the MacCAN library. [QTBUG-75405][QTBUG-76232] Expose the underling QIODevice used for Modbus communication, e.g. for setting the serial port hardware flow control (RTS/CTS). ماژول Qt WebEngine Updated to be based on Chromium 77 [QTBUG-74166] New API for control of QWebEnginePage life-cycle [QTBUG-56978] Methods and properties for download item to get suggested name, and change the download directory and file name [QTBUG-50420] New findTextFinished signal and corresponding QWebEngineFindTextResult and FindTextResult types to get extra information about the result of a text search [QTBUG-55110][QTBUG-51176] Added methods to QWebEngineCertificateError to asynchronously handle an error, and get a chain of digital certificates ماژول Qt Widgets QTextEdit and QTextBrowser now support Markdown format (CommonMark and GitHub dialects) as an alternative to HTML. Includes the GitHub checklist extension, such that you can click to toggle checkboxes if the widget is editable. QTextBrowser::setSource() detects Markdown based on the file extension. تغییرات در پلتفرم اندروید Android needs NDKr20+ Added multi ABI build in one go. By default it will build for all android supported abis (arm64-v8a, armeabi-v7a, x86_64, x86). The user can control which ABIs he wants to use via ANDROID_ABIS qmake variable. Added support for the new AAB package format, which allows deploying a single application bundle to Google Play which contains binaries for all supported ABIs. ماژول جدید Qt Quick Timeline کیوت برای اتوماسیون Qt KNX Extended support for KNXnet/IP Core V2 (discovery, tunneling, routing, security) Qt MQTT Added support for using QSslConfiguration when connecting encrypted Added autoKeepAlive property to enable manual connectivity checks Qt OPC UA Added tech preview for GDS client support Updated 3rdparty open62541 to version 1.0 Qt CoAP The module leaves the Tech Preview status behind and an API commitment is given.
  3. هفته گذشته
  4. جدیدا
  5. امیرمسعود نوراله

    ممنون از توضیح کامل و جامع.. مطلب بسیار عالی بیان شده و کاملا قابل لمس هست.
  6. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه آخر مواردی که در این جلسه یاد خواهید گرفت: کلاس‌ها و ساختار‌ها در این جلسه که جلسه‌ی آخر است، ما به بررسی مبحث کلاس‌ها و ساختار داده خواهیم پرداخت. اول از همه بگذارید تا تعریفی از کلاس و ساختار داده داشته باشیم و سپس به بقیه‌ی موارد خواهیم پرداخت. کلاس‌ها ( Classes ) چیستند؟ یک کلاس واقعی را در نظر بگیرید که از تعدادی زیاد یا کمی دانش‌آموزان تشکیل شده است. نام کلاس به عنوان مثال می‌تواند کلاس کامپیوتر-۲ باشد! و معلم و دانش‌آموزان هم اعضای آن کلاس هستند. معلم یک وظیفه دارد و دانش‌آموزان هم وظیفه‌ای دیگر...، در دنیای برنامه‌نویسی هم ما کلاس‌ها را داریم که در پروژه‌مان به کمک ما می‌آیند. کلاس‌ها، متشکل ویژگی‌ها ( Properties ) و متد‌ها ( Methods ) هستند که می‌توانند به صورت اختیاری به کلاس‌ها اضافه شوند. در سوئیفت شما می‌توانید بدون هیچ رابط یا فایلی، کلاس‌های خودتان را در یک فایل واحد تعریف کنید. نکته یک نمونه از یک کلاس، به طور معمول به عنوان یک شئ ( Object ) شناخته می‌شود؛ اما کلاس‌ها و ساختار به دلیل این‌که از نظر عملکرد با سایر زبان‌های برنامه‌نویسی، مثل هم هستند، از اصطلاح عمومی‌تر آن، یعنی نمونه استفاده می‌شود. ساختار داده ( Structure ) چیستند؟ ساختار داده هم دقیقا مانند کلاس‌ها هستند؛ که ثوابت، متغییرها و توابع را در خود ذخیره می‌کنند. تعریف کلاس‌ و ساختار نحوه‌ی تعریف کلاس و ساختار در سوئیفت، مشابه هم است! به این مثال دقت کنید؛ class Languages { // Class definition goes here } struct DataTypes { // Structure dfinition goes here } همان‌طور که مشاهده کردید، ما با استفاده از کلمه‌ی کلید class یک کلاس به نام Languages تعریف کرده و سپس با دو براکت {} باز و بسته، می‌توانیم کُد‌های خودمان را بنویسیم. در مقابل می‌بینید که تعریف ساختار داده، مشابه کلاس است! ابتدا کلمه‌ی کلید struct را نوشته و سپس یک نام برای آن انتخاب می‌کنیم. قوائد نام‌گذاری در کلاس‌ها و ساختار داده، به صورت پاسکال کِیس ( PascalCase ) هستش. یعنی حرف اول هر کلمه می‌بایست بزرگ نوشته شود و مابقی کلمه به حروف کوچک. مانند؛ ProgrammingLanguages. با یک مثال بهتر و شفاف‌تر، این موضوع را برای شما قابل‌ درک‌تر خواهیم کرد؛ class FullName { var first_name : String? var last_name : String? // Set function func setFullName(_ first_name : String, _ last_name : String ) { self.first_name = first_name self.last_name = last_name } // Get function func getFullName() -> String { return "First name is : \(self.first_name!)\n" + "Last name : \(self.last_name!)" } } struct DataTypes { let _age : Int let _name : String let _average : Double } let _full_name = FullName() let _datatype = DataTypes(_age: 20,_name: "Mohammad",_average: 20.0) _full_name.setFullName("Mohammad", "Abasi") print(_full_name.getFullName()) print("Age is : \(_datatype._age)") print("First name is : \(_datatype._name)") print("Average is : \(_datatype._average)")
  7. محمد صادق ابراهیم پور

    با سلام ممنون از کتاب بسیار خوبتان اگه امکان داره در مورد پیکربندی و انتشار برنامه در rasbian هم قرار بدید اون روشی که برای لینوکس گفتید برا رزبین جواب نمیده ممنون
  8. کامبیز اسدزاده

    این یک Log است.
  9. کامبیز اسدزاده

    مشکل به خاطر عدم به‌روز‌رسانی مخازن است. لطفاً با v-p-n وارد شوید و سعی کنید مخازن را به‌روز‌رسانی کنید. سپس در حین qmake و کامپایل gardle توسط خود کیوت کریتور دریافت و ادغام خواهد شد.
  10. حسین بیگی

    سلام دوربین های تحت شبکه یک لینک برای تصویر استریم شده در اختیار کاربران قرار میدن طبق تحقیقاتی که کردم یادم اگه لینک رو به opencv بدید باید بخونه اما هنوز فرصت برای تستش پیدا نکردم //--- INITIALIZE VIDEOCAPTURE VideoCapture cap; cap.open("rtsp://192.168.x.x/1") "); تو قسمتی که قرار دوربین رو باز کنه آدرس استریم اون دوربین رو بدید. همچنین ممنون میشم که من رو هم از نتیجه مطلع کنید
  11. کامبیز اسدزاده

    سلام، خطای لینکر مبنی بر این است که فایل ساخته شده‌ی کتابخانه قابل شناسایی نیست. قبل از هر چیز دستورات زیر را به ترتیب برای کامپایل (ساخت) کتابخانه‌ی tz اجرا کنید تا فایل‌ libtz.a ساخته شود. mkdir build cd build cmake ../ cmake -DENABLE_DATE_TESTING=ON --build . make در نهایت وارد یک پروژه‌ی جدید بشید تا برای افزودن کتابخانه به صورت زیر عمل کنید: فایل libtz.a را در یک پوشه‌‌ای با نام lib و محتوای کتابخانه را در پوشه‌ای با نام include در کنار پروژه‌ی خودتان قرار دهید. به دلیل اینکه این کتابخانه وابسته‌ی کتابخانه‌ی curl است، مطمئن شوید که آن نیز بر روی سیستم شما نصب باشد. دستورات زیر را در سی‌میک برای افزودن کتابخانه‌ی libtz بنویسید. add_library( date ${CMAKE_CURRENT_SOURCE_DIR}/include ) link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) target_link_libraries(${PROJECT_NAME} PRIVATE tz) احتمالاً خطایی برای عدم توانایی لینک‌سازی با curl دریافت کنید، در این صورت دستورات زیر را در سی‌میک اضافه کنید: FIND_PACKAGE(CURL) IF(CURL_FOUND) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) SET(requiredlibs ${requiredlibs} ${CURL_LIBRARIES} ) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR "Could not find the CURL library and development files.") ENDIF(CURL_FOUND) target_link_libraries(${PROJECT_NAME} PRIVATE curl) یک مثال از پروژه‌ای که من libtz را همراه کیوت ادغام کردم: cmake_minimum_required(VERSION 3.5) project(HelloCMake LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt5 COMPONENTS Widgets REQUIRED) FIND_PACKAGE(CURL) IF(CURL_FOUND) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) SET(requiredlibs ${requiredlibs} ${CURL_LIBRARIES} ) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR "Could not find the CURL library and development files.") ENDIF(CURL_FOUND) add_library( date ${CMAKE_CURRENT_SOURCE_DIR}/include ) link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) if(ANDROID) add_library(HelloCMake SHARED main.cpp mainwindow.cpp mainwindow.hpp mainwindow.ui ) else() add_executable(HelloCMake main.cpp mainwindow.cpp mainwindow.hpp mainwindow.ui ) endif() target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets) target_link_libraries(${PROJECT_NAME} PRIVATE tz) target_link_libraries(${PROJECT_NAME} PRIVATE curl) بعد از این نباید در زمان کامپایل خطایی رُخ دهد. موفق باشید!
  12. Mohammad Rahbari Doust

    سلام من میخوام کتابخونه tz رو توی پروژه کیوت استفاده بکنم ولی لینک ارور میده .ظاهرا باید از cmake استفاده کرد اما بلد نیستم. منابع خوب و سریعی هم اگر دارید معرفی کنید . تشکر
  13. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه دهم مواردی که در این جلسه یاد خواهید گرفت: توابع بی‌نام (‌ Closures ) و نو‌ع‌های شمارشی توابع بی‌نام ( Closures ) چیستند؟‌ به زبان ساده، توابعی هستند که نامی ندارند! این توابع در واقع به یک متغییر انتساب داده می‌شوند و بعد از آن مورد استفاده قرار می‌گیرند. سوال؛‌ تفاوت بین این توابع با توابع معمولی چیست؟ پاسخ؛ اگر در روند پروژه‌تان بخواهید که تابعی داشته باشید که فقط و فقط یک‌بار اجرا شده و پس از آن دیگر استفاده از آن نداشته باشید و یا بخواهید که یک تابع جزئی از کد شما نباشد، می‌توانید از توابع بی‌نام استفاده کنید. در واقع این نوع توابع، از آنجایی که نامی ندارند، باید به یک متغییر انتساب داده شوند تا بتوان آن‌ها را مورد استفاده قرار داد. اجازه بدهید که با یک مثال، به کنجکاوی شما پاسخ دهیم! در حالت کلی، Syntax ( نحو ) کلی تعریف یک تابع بی‌نام به این شکل است؛ { (Parameters) -> ReturnType in // Statments } همان‌طور که مشاهده می‌کنید، حالت کلی تعریف یک تابع بی‌نام به این صورت است. ابتدا شما باید دو آکولاد {} تعریف کنید که دستورات شما داخل این بدنه قرار می‌گیرند و سپس باید نوع دادهٔ برگشتی را تعیین کنید. نکتهٔ مهمی که وجود دارد، کلمهٔ کلیدی in است. این کلمهٔ کلید مشخص می‌کند که پارامتر‌های که به تابع می‌دهید، باید از هم جدا شوند! درواقع در حالت معمولی تعریف یک تابع، شما نیازی به این کلمهٔ کلیدی ندارید، اما در توابع بی‌نام برای اینکه‌ پارامتر‌ها از هم جدا شوند، استفاده از این کلمهٔ کلیدی الزامی و لازم است. اگر بخواهیم یک مثال بزنیم تا برایتان شفاف‌تر شود، فرض کنید می‌خواهیم نام و نام خانوادگی شخصی را گرفته، کمی آن را استایل‌دهی کنیم و سپس به آن را به تابع برگردانیم! برای این منظور، به این شکل عمل خواهیم کرد؛ یک تابع معمولی همان‌طور که در جلسهٔ قبل گفته شده، به این صورت تعریف می‌شود؛ func getFullName(_ first_name: String, _ last_name: String) -> String { let _full_name = "First Name is : \(first_name) and Last Name is : \(last_name)" return _full_name } print(getFullName("Mohammad", "Abasi")) // First Name is : Mohammad and Last Name is : Abasi اما این داستان در توابع بی‌نام کمی فرق می‌کند و تعریف آن به این صورت است؛ let _full_name = { (_ first_name: String, _ last_name: String) -> String in let _full_name = "First Name is : \(first_name) and Last name is : \(last_name)" return _full_name } print(_full_name("Mohammad", "Abasi")) ملاحضه می‌کنید که ما دو پارامتر از نوع رشته با نام‌های first_name و last_name تعریف کرده و نوع برگشتی آن را هم String ( رشته‌ ) قرار داده‌ایم، فراموش نکنید که حتما باید از کلمهٔ کلیدی in بعد از مشخص کردن نوع دادهٔ برگشتی استفاده کنید؛ در غیر اینصورت، با این خطا مواجه خواهید شد؛ main.swift:1:20: error: expected ',' separator var closuer = { (_ first_name: String, _ last_name: String) -> String ^ , main.swift:3:42: error: use of unresolved identifier 'first_name' let _full_name = "First Name is : \(first_name) and Last name is : \(last_name)" ^~~~~~~~~~ main.swift:3:75: error: use of unresolved identifier 'last_name' let _full_name = "First Name is : \(first_name) and Last name is : \(last_name)" ^~~~~~~~~ main.swift:1:15: error: unable to infer complex closure return type; add explicit type to disambiguate var closuer = { (_ first_name: String, _ last_name: String) -> String ^ پس آن ( in ) را فراموش نکنید. در بدنهٔ تابع، ما یک ثابت ( let ) تعریف کرده و سپس یک رشته کامل همراه با پارامتر‌ها به آن انتساب می‌دهیم و در آخر آن را به تابع برگشت ( return ) می‌دهیم. علاوه بر این‌که شما می‌توانید به این صورت عمل کنید ( انتساب تابع به متغییر ) می‌توانید بدون متغییر این‌کار را هم بکنید! فرض کنید می‌خواهیم دو عدد را با هم جمع بزنیم و فقط می‌خواهیم یک‌بار برایمان این‌کار انجام شود. برای این منظور به این صورت می‌توانیم عمل کنیم؛ let _addition = { (_ number_one: Double, _ number_two: Double) -> Double in return number_one + number_two }(10,20) print(_addition) // Result is = 30 یا print({ (_ number_one: Double, _ number_two: Double) -> Double in return number_one + number_two }(10,20)) // Result is = 30 همان‌طور که مشاهده می‌کنید، در دو حالت یکی هستند، تنها تفاوتی که وجود دارد، بعد از آخرین آکولاد ( { ) است. یعنی این قسمت؛ {(10,20)) // Result is = 30 شما در این قسمت و در بین دو پرانتز () به پارامتر‌هایی که تعیین کرده‌اید، آرگومان‌های خودتان را ارسال می‌کنید ( در این مثال؛ ۱۰ و ۲۰ ) و در نتیجه، به جای این‌که یک تابع برگشت داده شود، یک نتیجه را به دنبال خواهد داشت. اگر این‌کار را نکنید و با تابع print بخواهید ببینید که بدون آرگومان دادن، چه اتفاقی خواهد افتاد، نتیجه در خروجی یا تابع خواهد بود؛ print({ (_ number_one: Double, _ number_two: Double) -> Double in return number_one + number_two }) // Result is = (Function) اگر قصد شما این است که نتیجه را در همان زمان مورد استفاده قرار دهید، استفاده از دو روش بالا، جوابگوی شماست و اگر خواستید یک تابع داشته باشید که یک کار مشخص را انجام داده و بعد از آن دیگر مورد استفاده قرار نگیرد، می‌توانید از حالت اول استفاده کنید. مرتب سازی عناصر با توابع بی‌نام ( Sort Elements in Closures ) شما می‌توانید عناصر یک آرایه را با استفاده از توابع بی‌نام، مرتب‌ کنید! در این نوع مرتب‌سازی بستگی به شما دارد که چگونه بخواهید عمل مرتب‌سازی را انجام دهید. بگذارید مثالی را ارائه دهیم تا موضوع را بیشتر و بهتر درک کنید و سپس به سراغ توضیحات تکمیلی خواهیم رفت؛ let _numbers = [1,3,2,4,6,5] var sorted_numbers = _numbers.sorted(by: { (_ number_one: Int, _ number_two: Int) -> Bool in return number_one < number_two }) print(sorted_numbers) همان‌طور که در قطعه کد بالا مشاهده می‌کنید، ابتدا یک آرایه تعریف کرده‌ایم ( [let _numbers = [1,3,2,4,6,5 ) و سپس یک متغییر دیگر با نام ( sorted_numbers ) تعریف کرده‌ که خود یک آرایه است! به این دلیل می‌گویم آرایه، که متد sotred یک آرایه را برمی‌گرداند. کار این متد مرتب‌سازی یک آرایه براساس آرگومانیست که شما به آن می‌دهید. این متد از کلاس آرایه است ( numbers.sorted_ ). بیاید به تشریح آرگومانی که دریافت می‌کنید، بپردازیم. البته ملاحظه می‌کنید که آرگومان، یک تابع بی‌نام است! شما می‌توانید با برچسب :by تابع بی‌نام خود را تعریف کرده و سپس متد sorted کار خود را شروع می‌کند و به مرتب‌سازی اعضای آرایه می‌پردازد. شما در تابع بی‌نامی که تعریف می‌کنید باید دو پارامتر را در نظر بگیرید. واضح است که دو پارامتر برای اعضای آرایه است که قرار است در بدنهٔ تابع مورد بررسی قرار گیرند و نتیجهٔ نهایی به تابع sorted داده شود. ما دو پارامتر به نام‌های ( number_one, number_two‌ ) تعریف کرده‌ایم که هر دو از نوع عددی هستند ( با توجه به نوع دادهٔ آرایه خود باید پارامتر‌ها را مشخص کنید ) و سپس نوع برگشتی را Bool انتخاب کردیم که نتیجهٔ true یا false را برمی‌گرداند. و در نهایت کلمهٔ کلید in که برای توابع بی‌نام همان‌طور که گفته‌ بودیم، لازم و اجباریست. در بدنهٔ تابع، چیزی جز یک دستور نخواهیم داشت! با استفاده از دستور return number_one < number_two می‌گویم اگر عدد اوله ( عضو اول آرایه ) از عدد دوم (‌ عضو دوم آرایه ) کوچک‌تر بود، نتیجه true ( درست ) را برگردان و در غیر این‌صورت false ( نادرست ) را برگردان. از آنجایی که عدد اول یعنی ۱، کوچک‌تر از عدد دوم یا همان عضو ۳ است، بنا‌براین true یا همان درست به تابع برگشت داده می‌شود که در نتیجه عدد ۱ از آرایه، در جای خود باقی می‌ماند. اما در تکرار بعدی، عدد اول یعنی ۳ کوچک‌تر از عدد ۲ نیست و نتیجهٔ false یا نادرست برگشت داده خواهد شد که در نهایت عدد ۲ در جایگاه عدد ۳ قرار می‌گیرد و الی آخر... . این هم یک نمونه از کاربرد توابع بی‌نام بود که با متد sorted از کلاس آرایه همکاری کرد تا به مرتب‌سازی یک آرایه بپردازد. شما همچنین می‌توانید از خلاقیت خود استفاده کنید و کد‌نویسی زیباتر و خلاقانه‌تری انجام دهید. شروع کنید و لذت ببرید! نوع‌های شمارشی ( Enumerations ) چیستند؟ نوع‌های شمارشی یک نوع داده‌ایست که شامل گروهی از داده‌ها مرتبط با هم هستند! در مقابل هم باعث خوانایی بیشتر پروژهٔ نرم‌افزاری خواهد شد. بگذارید باز هم با یک مثال شروع کنیم تا در توضیحات برای شما عزیزان، شفاف‌تر و قابل‌ درک‌تر شود. برای تعریف یک enum می‌بایست از Syntax زیر استفاده کنیم؛ enum EnumName { // Enumeration values are described here } همان‌طور که مشاهده می‌کنید، حالت کلی تعریف آن به این صورت است. ابتدا کلمهٔ کلید enum را آورده که مشخص کنندهٔ تعریف یک نوع شمارشی است و سپس باید یک نام هم برای آن در نظر بگیرید ( حتما نامی را انتخاب کنید که بیان‌گر کاری است که انجام می‌دهد )‌ و در نهایت در بین دو آکولاد {} می‌توانید داده‌های خود که مرتبط با نام است تعریف کنید! به این مثال به دقت نگاه کنید؛‌ enum ProgrammingLanguages { case C case Java case Rust case R case Python case PHP case Javascript case HTML case Perl case Ruby case Swift } var language = ProgrammingLanguages.C print("\(Language) is programming language!") // C is programming language! در قطعه کد بالا همان‌طور که ملاحضه کردید، ما یک نوع شمارشی به نام ProgrammingLanguages تعریف کرده‌ایم که به معنی‌ زبان‌های برنامه‌نویسی است و درواقع داده‌های آن هم مرتبط با برنامه‌نویسی هستند نه چیزی دیگر! به عنوان مثال ( C,Java,Rust و ... ) همهٔ آن‌ها مرتبط با برنامه‌نویسی هستند و به خاطر همین است که ما به آن‌ها می‌گویم گروهی از داد‌ه‌ها که با هم مرتبط هستند! اگر یادتان باشد، گفته بودیم که نوع‌های شمارشی در پروژهٔ شما می‌تواند خوانایی بالایی ایجاد کند. اگر مثال بالا را بخواهیم با آرایه‌ها بنویسیم، به این صورت خواهد بود؛ var programming_languages = ["C", "C++", "Java", "Rust", "R", "Javascript", "PHP", "Swift", "Ruby", "Perl", "Python", "HTML"] آیا با این روش می‌توانید سریع به دادهٔ مورد‌نظر خود دسترسی داشته باشید؟‌ مسلما خیر! چرا که به این صورت اگر بخواهید داد‌ه‌های خود را تعریف کنید و از آن‌ها استفاده کنید، وقت و انرژی زیادی از شما خواهد گرفت! پیدا کردن ایندکس ( به عنوان مثال R ) وقت و انرژی از شما خواهد گرفت. اما اگر با نوع‌های شمارشی بنویسید، علاوه بر کد‌نویسی زیبا‌تر و تمیز‌تر، دسترسی سریع‌تر و راحت‌تری هم خواهید داشت. به این مثال دقت کنید؛ enum Languages { case C case Java case Rust case R case Python case PHP case Javascript case HTML case Perl case Ruby case Swift } var language = Languages.Javascript switch language { case .C: print("C is programming language!") case .Java: print("Java is programming language!") case .Rust: print("Rust is programming language!") case .R: print("R is programming language!") case .Python: print("Python is programming language!") case .PHP: print("PHP is programming language!") case .Javascript: print("Javascript is programming language!") // Javascript is programming language! case .HTML: print("HTML is programming language!") case .Perl: print("Perl is programming language!") case .Ruby: print("Ruby is programming language!") case .Swift: print("Ruby is programming language!") } در مثال بالا مشاهده می‌کنید که شما می‌توانید به راحتی با تعریف یک نوع شمارشی، به راحتی هرچه‌ تمام‌تر به داده‌های آن با استفاده از نام آن‌ها دسترسی داشته باشید. در دستورات شرطی مثل switch دقت کنید که ابتدا باید مقدار دادهٔ مورد نظرتان از نوع شمارشی که تعریف کرده‌اید را به متغییر انتساب داده و سپس به عنوان یک مقدار به دستور switch بدهید تا بتواند آن را بررسی کند! چرا که در switch مقدار باید ثابت باشد نه متغییر! و سپس در بدنهٔ شرط، با استفاده از دات ( . ) و نام دادهٔ مورد‌نظر در caseها ( case .HTML ) می‌توانید داده‌‌ی ثابتی که در switch تعریف کردید را با مقادیر caseها مقایسه کنید و در نهایت خروجی متناسب با آن را مشاهده کنید. امیدواریم که این جلسه مورد رضایت شما قرار گرفته باشد.
  14. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه نُهم مواردی که در این جلسه یاد خواهید گرفت: توابع ( Functions‌ ) توابع چیستند؟‌ این چیزی را که می‌گوییم، چند لحظه تصور کنید و دوباره به خواندن این مطلب ادامه دهید! فرض کنید پروژه‌ای را شروع کردید و در بخشی از پروژه‌ی خودتان، متوجه‌ی این شدید، بخشی از کد نیاز به تکرار در جای دیگر دارد... از نظر شما، این کار تکرار کُد؛ بیهوده و بی‌معنی است. پس راه‌حل چیست؟ توابع، یکی از روش‌های جلوگیری از این کار‌هاست! گفتیم یکی از روش‌ها، چرا که روش دیگری نیز وجود دارد که عهده‌دار آن کلاس‌ها هستند. توابع به شما کمک می‌کنند که کدهای کمتری بنویسید و بیشتر استفاده کنید! شما با یک‌بار نوشتن یک قطعه کد، می‌توانید در ادامه‌ی پروژه‌ی بی‌نهایت از آن قطعه کد استفاده کنید! این همان کاریست که توابع انجام می‌دهند. از جلمه مزایای استفاده از توابع می‌توان به موارد زیر اشاره کرد؛ جلوگیری از تکرار کدها یا همان قابلیت استفاده مجدد که می‌توانیم یک‌بار یک تابع را نوشته و چندین بار در جاهای مختلف برنامه از آن استفاده کنیم سرعت بیشتر جدا نگه‌داشتن بخش‌های مختلف یک برنامه ( از لحاظ منطقی و همچنین از دید برنامه‌نویسی کاری منطقی نیست که یک پروژه را به بخش‌های مختلفی تقسیم نکنیم و همه‌ی کار‌ها را به یک بخش محول کنیم ) خوانایی بیشتر برنامه خطایابی آسان‌تر می‌شود و مواردی دیگر ... البته مواقعی هم هستند که توابع باعث می‌شوند که سرعت اجرای برنامه کاهش پیدا کند! مانند موقعی که شما به توابع پارامتر می‌دهید، و در یک فراخوانی ( Calling ) آرگومان‌های خود را به آن ارسال می‌کنید، سرعت اجرا پایین می‌آید، اما موقعی که کد‌ها را مستقیما می‌نویسید، حافظه درگیر فراخوانی و گرفتن ورودی نمی‌شود و مستقیم، روی فراخوانی و اجرای کدها تمرکز می‌کند. نحوه‌ی تعریف یک تابع ابتدا به نحوه‌ی تعریف یک تابع می‌پردازیم و در ادامه هر بخش را توضیح خواهیم داد؛ func FunctionName ( Parameters ) -> ReturnType { // Statements return parameters } همان طور که مشاهده می‌کنید، تعریف کلی یک تابع در سوئیفت به این شکل است. از کلمه‌ی کلیدی func شروع می‌کنیم. این کلمه‌ی کلید که کوتاه‌ شده‌‌ی ( function )‌ به معنای تابع است، به کامپایلر اطلاع می‌دهد که برنامه‌نویس می‌خواهد یک تابع تعریف کند! درواقع این نشانگر تعریف یک تابع است. بعد از این کلمه‌ی کلید باید یک نام برای تابع خودتان انتخاب کنید. بدون نام شما چطور می‌خواهید به تابع خود دسترسی داشته باشید؟‌ در انتخاب نام آزاد هستید، اما علاوه‌ بر رعایت نکات نامگذاری توابع، نام تابع شما باید نمایانگر کاری است که تابع شما قرار است انجام دهد. بعد از نام، شما باید در بین دو پرانتز () پارامتر‌های ( Parameters )‌ را مشخص کنید! پارامتر‌ها، متغیر‌هایی هستند که در هنگام تعریف تابع، مشخص می‌شوند و برای گرفتن مقداری خاص از کاربر استفاده می‌شوند. بعد از مشخص کردن پارامتر‌ها، باید نوع برگشتی ( ReturnType ) را مشخص کنید. فرض کنید می‌خواهید عمل فیلتر را روی تصویر انجام دهید! اگر بخواهید بعد از اعمال فیلتر روی تصویر توسط تابع، تصویر فیلتر‌شده را در اختیار داشته باشید، نیاز خواهید داشت که آن را در قالب خاصی به تابع برگردانید! و اگر فقط تصویر فیلتر‌شده را خواستید نمایش بدهید، به طور مستقیم و بدون نوع برگشتی می‌توانید این‌کار را انجام دهید. منظور از قالب خاص، همان نوع داده‌ای است که به تابع برگردانده می‌شود. در انتها، شما دربین دو آکولاد {}، می‌توایند دستورات خود را بنویسید. فراموش نکنید که در آخر دستورات، باید return parameters را بنوسید! چرا که شما اگر نوع برگشتی را مشخص کرده باشید، باید مقداری هم برگشت دهید و این‌کار با استفاده از این دستور امکان‌پذیر است. البته به‌جای parameters باید داده‌ی مرتبط با نوع برگشتی را بنویسید. توضیحات کافیست؛ بیاید یک مثال عملی را با هم ببینیم؛ func getWebSite(website_name: String) -> String { let platform_name_and_website_name = "www.fanoox.com + " + first_name = " :) -> Perfect!" return platform_name_and_website_name } print(getWebSite(website_name: "www.iostream.ir") // www.fanoox.com + www.iostream.ir = :) -> Perfect همان‌طور که مشاهده کردید، شما یک اسم برای پارامتر انتخاب می‌کنید و به دنبال آن نوع داده را انتخاب می‌کنید. مانند؛ website_name: String و نوع برگشتی را هم که ملاحضه می‌کنید، با علامت <- و سپس نوع داده‌ی مورد‌نظر ( در این مثال؛ String ) مشخص می‌کنید. بعد از آن شما می‌توانید در بین دو آکولاد باز و بسته {}، کد‌های خود را در درون آن‌ها بنویسید و در آخر فراموش نکنید باید یک مقداری را برگشت دهید که با دستور return و مقدار برگشتی (‌ در این مثال؛ platform_name_and_website_name‌ ) را تعیین کنید. دقت کنید که مقداری را که برگشت می‌دهید باید با نوع داده‌ای که در تعریف تابع مشخص شده است، یکسان‌ باشد. برای فراخوانی یا همان صدا زدن تابع، همان‌طور که می‌بینید، باید فقط اسم تابع را بیاورید و بعد از آن اگر پارمتری الزامی بود، به آن بدهید. نام پارارمتر را هم باید حتما با نام باشد! ( "website_name: "www.iostream.ir ) که غیر از این باشد، کامپایلر از شما خطا خواهد گرفت. همچنین شما می‌توانید یک تابع بدون پارامتر و با نوع برگشتی داشته باشید و یا برعکس! یعنی بدون پارامتر و نوع برگشتی. ابتدا یک تابع با نوع برگشتی و بدون پارامتر مثال خواهیم زد؛ func getWebSite() -> String { let platform_name = "www.fanoox.com -> :) -> Perfect!" return platform_name } print(setWebSite()) // www.fanoox.com -> :) -> Perfect همان‌طور که مشاهده کردید، ما می‌توانیم یک تابع بدون پارامتر و در عین حال با نوع برگشتی داشته باشیم. در مقابل اگر هیچ‌کدام از این موارد را نخواهیم؛ یعنی بدون پارارمتر و نوع برگشتی، به این صورت عمل خواهیم کرد؛ func getWebSite() { print("www.fanoox.com -> :) -> Perfect!") } getWebSite() // www.fanoox.com -> :) -> Perfect مشاهده می‌کنید که در قطعه کد بالا نه خبری از نوع برگشتی است و نه پارارمتر و شما به طور مستقیم بر روی داده‌ها کار می‌کنید. پارامتر‌های چندگانه فرض کنید می‌خواهید یک ماشین حساب طراحی و کد‌نویسی کنید! اگر بخواهید از توابع استفاده کنید، نیاز به این خواهید داشت که دو عدد به همراه یک آپریتر ( Operator ) به تابع ارسال کنید. در این صورت ما از پارامتر‌های چندگانه استفاده می‌کنیم. یعنی شما می‌توانید تا بی‌نهایت، پارارمتر در هنگام تعریف تابع، بنویسید. بیاید یک ماشین حساب کوچک، با هم طراحی کنیم؛ func Calculator(number_one: Double, number_two: Double, operator_ch: Character) -> Double { if operator_ch == "+" { return number_one + number_two } else if operator_ch == "-" { return number_one - number_two } else if operator_ch == "*" { return number_one * number_two } else if operator_ch == "/" { return number_one / number_two } else { return 0 } } print(Calculator(number_one: 10, number_two: 30, operator_ch: "+")) // 40 // 40 + 30 = Chelsea print(Calculator(number_one: 10, number_two: 20, operator_ch: "+")) // 30 به همین سادگی ما توانسیتم یک ماشین حساب کوچک طراحی کنیم! شما می‌توانید حتی با اضافه کردن ویژگی‌های جدید‌تر، آن را گسترش دهید. اما نکته‌ی مهم‌تر از آن، پارارمترهای چندگانه‌ هستند که مشاهده کردید. ما در هنگام تعریف تابع ماشین‌حساب،‌ از سه پارارمتر استفاده کردیم! دو عدد و یک کارکتر. دو عدد که کاربر وارد کرده و کارکتر هم همان یک از ۴ آپریتر (‌+,-,/,* )‌ هستند که درواقع چهار عمل اصلی در محاسبات ریاضی را انجام می‌دهند. همچنین شما می‌توانستید بدون این‌که بخواهید نوع برگشتی را مشخص کنید، مستقیم نتیجه را نمایش دهید؛ func Calculator(number_one: Double, number_two: Double, operator_ch: Character) { if operator_ch == "+" { print(number_one + number_two) } else if operator_ch == "-" { print(number_one - number_two) } else if operator_ch == "*" { print(number_one * number_two) } else if operator_ch == "/" { print(number_one / number_two) } else { print(0) } } Calculator(number_one: 10, number_two: 30, operator_ch: "+")) // 40 // 40 + 30 = Chelsea Calculator(number_one: 10, number_two: 20, operator_ch: "+")) // 30 می‌توانید تا بی‌نهایت پارامتر اضافه کنید، اما دقت کنید که این‌کار باعث اشغال منابع از حافظه می‌شود، پس به تعداد مورد نیاز، پارامتر اضافه کنید. نام پارامتر‌ها و برچسب‌‌های آرگومان‌ها در حالت پیش‌فرض اگر تابعی را تعریف کنید و پارامتری را هم برای آن مشخص کنید، در هنگام فراخوانی ( Calling ) موظف هستید که اسم نام متغییر در پارامتر که برچسب خوانده می‌شود را هم بیاورید و سپس مقدار را در مقابل آن بنویسید؛ func getFirstName(first_name: String) -> String{ return "FirstName is : \(first_name)" } func getLastName(last_name: String) -> String { return "LastName is : \(last_name)" } func getStyleMe() -> String{ return " ): + :) with code = :) -> Perfect! " } print(getFirstName(first_name: "Mohammad"), getLastName(last_name: " Abasi"), getStyleMe())) // Mohammad Abasi ): + :) with code = :) -> Perfect! همان‌طور که مشاهده کردید، در هنگام فراخوانی موظف به نوشتن برچسب نام متغییر‌ها در هنگام تعریف تابع نیز می‌باشید! ( در این مثال، first_name و last_name‌ ) البته این حالت پیش‌فرض حالت سوئیفت است و شما می‌توانید از این حالت پیش‌فرض استفاده نکنید ( در ادامه بیشتر توضیح خواهیم داد ) و در این مورد سوئیفت، کاملا دست شما را باز نگه داشته است. اگر بخواهید از دو برچسب در پارامتر‌ها استفاده کنید و یکی از برچسب‌ها فقط نقش گرفتن مقدار را داشته باشد، به این صورت می‌توانید عمل کنید؛ func getFullName(name first_name: String, last last_name: String) -> String { return "FirstName is : \(first_name) and LastName is : \(last_name)" } print(getFullName(name: "Mohammad", last: "Abasi")) // FirstName is : Mohammad and LastName is : Abasi برچسب‌های اولیه ( name, name ) همان نقش پیش‌فرض را برای تابع بازی می‌کند و مقدار‌های رشته‌ای Mohammad و Abasi در برچسب‌های last_name و first_name ذخیره می‌شوند. ا گر نخواستید از برچسب‌ها استفاده کنید و درواقع آن‌ها را مزاحم دانستید؛‌ می‌توانید از علامت آنِدرلاین‌ ( ـ ) برای این منظور استفاده کنید؛‌ func getFullName(_ first_name: String, _ last_name: String) -> String { return "FirstName is : \(first_name) and LastName is : \(last_name)" } print(getFullName("Mohammad", "Abasi")) // FirstName is : Mohammad and LastName is : Abasi دقت کنید که حتما شما باید یک نام یا همان برچسب دوم برای ذخیره شدن مقدار آرگومان ( در این مثال؛ first_name, last_name ) بنویسید. همان‌طور هم که ملاحضه کردید، در هنگام فراخوانی تابع، ما دیگر از برچسب استفاده نکردیم! بلکه با گذاشتن علامت آندِرلاین ( ـ ) کامپایلر برچسب‌ها را نادیده گرفته است. پارامتر‌های پیش‌فرض شما می‌توانید از مقادیر پیش‌فرض برای توابع خود استفاده کنید! اگر تابعی را که تعریف می‌کنید، مطمعن نیستید که کاربر مقداری را وارد می‌کند یا نه، می‌توانید از مقادیر پیش‌فرض مورد‌نظر خود برای این منظور استفاده کنید؛ func getFullName(_ first_name: String = "Mohammad", _ last_name: String = "Abasi") -> String { return first_name + " " + last_name } print(getFullName()) // Mohammad Abasi print(getFullName("Mehdi")) // Mehdi Abasi print(getFullName("Hamed", "Doosti")) // Hamed Doosti همان‌طور که مشاهده کردید، شما می‌توانید از یک یا چند مقدار ( بستگی به نیاز ) به صورت پیش‌فرض استفاده کنید که اگر احیانا کاربر، مقداری را وارد نکرد، کامپایلر از مقدار پیش‌فرض که تعیین کردید، استفاده می‌کند. در فراخوانی اول مشاهده می‌کنید که چون برای هر دو پارامتر first_name و last_name مقداری مشخص تعیین کردیم، بدون هیچ مسئله‌ای، خروجی برای ما نمایش داد می‌شود و در فراخوانی‌های ("getFullName("Mehdi نگران پارامتر دوم نیستیم! چرا که مقدار پیش‌فرض Abasi را دارد. و همچنین ("getFullName("Hamed", "Doosti هم مقادیر آن جایگزین با مقادیر Mohammad و Abasi می‌شود. پارامتر‌های متغییر ( Variadic Parameters ) شاید با خود بگویید؛ پارامتر‌های متغییر دیگر چیستند؟! در اکثر مواقع ما می‌خواهیم فقط و فقط با یک پارامتر در یک تابع، تا بی‌نهایت بتوانیم هنگام فراخوانی، مقادیر ارسال کنیم! در برخی از زبان‌های برنامه‌نویسی این امکان فراهم شده است و سوئیفت هم یکی از این‌ زبان‌هاست. اگر یک تابع داشته باشید که نام مشتریان را دریافت کند و آن‌ها را در دیتابیس ( پایگاه داده ) ذخیره کند، در این صورت شما نمی‌توانید از هزاران پارامتر برای این منظور استفاده کنید و کاری غیر منطقی است. بنابر‌این با استفاده از الگوی زیر، شما می‌توانید با یک پارامتر، تا بی‌نهایت نام مشتری به تابع ارسال کنید؛ var database = Array<String>() func storeCustomers(_ customer: String ... ) { database = customer } func getCustomer(_ index: Int = 0) -> String { return database[index] } storeCustomers("Mohammad", "Mehdi", "Ahamad", "Nahid", "Zhara", "Ali", "Hamed", "Daryoos", "Karim") print(getCustomer(1)) همان‌طور که مشاهده می‌کنید، در تابع storeCustomers پارامتر ... customer: String _ استفاده کردیم که بیانگر تابعی با پارامتر‌های متغییر است! این پارامتر همانند یک آرایه عمل می‌کنید و تنها تفاوت آن غیرقابل تغییر بودن و به اصطلاح فقط خواندنی (‌ Read-Only ) است. اگر سعی کنی که customer را تغییر دهید، با این خطا مواجه خواهد شد؛‌ cannot assign to value: 'customer' is a 'let' constant، که نشانگر آن است، این آرایه ( در این مثال؛ customer ) یک ثابت یا همان let است و قابل تغییر نیست. سه نقطه‌ای ( ... ) که مشاهده می‌کنید، به کامپایلر این پیغام را می‌رساند که این تابع قرار است بیش از یک مقدار دریافت کند و یا همان مقادیر متغییر! پس آن را فراموش نکنید. در قطعه کد بالا، ما یک شبیه‌سازی ذخیره و بازیابی نام کاربر را انجام داده‌ایم که شما می‌توانید با خلاقیت خودتان، چیزی بهتر و خارق‌العاده‌تر بنویسید! پس با این روش می‌توانید پارامتر‌های متغییر ارسال کنید و نگران این نباشید که هزاران پارامتر تعریف کنید؛ به این نکته‌ هم دقت کنید که حتما حتما در این نوع تعریف تابع، باید از آندِرلاین (‌ - ) استفاده کنیم در غیر این‌صورت با خطای کامپایلر مواجه خواهید شد. نکته شما نمی‌توانید بیش از یک variadic parameters در تابع خود داشته باشید! فقط و فقط مجاز به تعریف یک variadic parameter هستید. پارامتر با ارجاع اگر شما یک متغییر خارج از تابع داشته باشید و بخواهید همان متغییر را به تابع بفرستید، عملا مقدار کُپی شده است! این یعنی این‌که اگر تغییری در متغییر داخل تابع که به عنوان متغییر داخلی ( Local ) شناخته می‌شود؛ صورت گیرد، متغییر سراسری ( Global ) هیچ تغییری نمی‌کند! چرا که فقط مقدار کپی شده است نه ارجاع! اگر بخواهیم همان متغییری را که به تابع ارسال می‌کنیم، همزمان با تغییر متغییر در تابع،‌ تغییر کند، به این صورت عمل خواهیم کرد؛ var first_name : String = "Mohammad" func Local(_ first_name: inout String) -> String{ first_name = "Mehdi" return first_name // Current value } func Global() -> String { return first_name // Value has changed } print(Local(&first_name)) // Mehdi print(Global()) // Mehdi همان‌طور که مشاهده کردید، ابتدا یک متغییر سراسری ( "var first_name : String = "Mohammad ) تعریف کرده و سپس دو تابع به نام‌های Local و Global ایجاد کردیم. اولین تابع یعنی؛ Local برای دریافت مقدار از طرف کاربر است و اگر توجه کرده باشید ما در پارامتر تابع از کلمه‌ی کلید inout استفاده کرده‌ایم! این کلمه‌ی کلیدی برای دریافت مقدار با ارجاع است و حتما باید آن را بعد از نام پارامتر و دو نقطه ( : ) بیاورید و در نهایت نوع داده را مشخص کنید. در مقابل تابع Global است که مقدار تغییر داده شده‌ی متغییر سراسری را تغییر می‌دهد ( همان متغییر که بالاتر از تمام توابع نوشته شده است ). در هنگام فراخوانی تابع دقت داشته باشید که اگر از ارجاع ( کلمه‌ی کلید inout ) استفاده کرده باشید، باید حتما از آپریتر & قبل از فرستادن متغییر به تابع استفاده کنید! چرا که قرار است آدرس متغییر را به تابع ارسال کند و در این صورت است که می‌توانیم عمل ارجاع و تغییر متغیر سراسری را انجام دهیم. هنگامی که شما متغییر first_name& را به این شکل به تابع ارسال می‌کنید، درواقع آدرس را ارسال می‌کنید و آدرس حافظه‌ی متغیر داخل تابع، با متغییر سراسری یکی است! وقتی متغییر داخلی تغییر کند، متغییر سراسری هم تغییر می‌کند. در فراخوانی توابع هم مشاهده می‌کنید که علاوه بر این‌که متغییر داخلی مقدار تغییر کرده (‌ تابع Local )، متغییر سراسری هم مقدارش تغییر کرده است ( تابع Global ). بنابر‌این ما می‌توانیم به این صورت عمل ارجاع و تغییر متغییر از بیرون تابع را انجام دهیم. نکته شما نمی‌توانید از مقادیر پیش‌فرض در این نوع تعریف تابع استفاده کنید و همچنین هم مجاز به استفاده از مقادیر متغییر ( variadic parameter ) نخواهید بود. استفاده از نوع‌های تابعی سوئیفت، قابلیت‌های جالب و در عین حال ساده دارد! یکی از این قابلیت‌ها نوع تابعی است! ما اگر می‌خواستیم یک متغییر معمولی تعریف کنیم، به این شکل عمل می‌کردیم؛ var number : Double = 20.0 حال ما می‌توانیم یک متغییر با حالت تابع داشته باشیم! به این مثال دقت کنید؛‌ func addition(number_one: Double, number_two: Double) -> Double { return number_one + number_two } let _addition_function : (Double, Double) -> Double = addition print(_addition_function(10.0,10.0)) // 20.0 احتمالا شما هم مثل عاشق پیچیدگی هستید! اینطور نیست؟! در قطعه کد بالا تابع را تعریف کرده‌ایم که عملا کاری با آن نداریم! ما به متغییر تابعی‌مان _addition_function کار خواهیم داشت و می‌خواهیم آن را موشکافی کنیم! در ابتدا شما می‌توانید از کلمه‌ی کلید let و یا var استفاده کنید و سپس با دو نقطعه ( : ) که به آن کالُن هم گفته می‌شود، در بین دو پرانتز () نوع‌ داده‌هایی را که در پارامتر تابع تعریف کردید، می‌نویسید. و سپس همانند نوع برگشتی در تابع ( Double <- ) باید بعد از پرانتز بسته شده (‌ Double, Double ) آن را بنوسید و سپس تابعی را که از قبل تعریف کردیم به آن بدهیم! توجه کنید فقط و فقط نام تابع کافیست و نیازی به پرانتز نیست. الان شما متغییر با حالت تابع‌ماننده دارید و می‌توانید از آن استفاده کنید. هنگامی که متغییر خود را فراخوانی می‌کنید، باید آرگومان‌های الزامی را به آن بدهید؛ همانند مثال بالا ( (addition_function(10.0, 10.0 ). نکته‌ی دیگر که وجود دارد این است که دیگر نیازی نیست، نام پارامتر‌ها را هم برای مقدار‌دهی بیاورید! در صورتی که با تعریف یک تابع معمولی، شما موظف بودید که نام پارامتر‌ها هم برای مقداردهی بنویسید. توابع تودرتو ( Nested Functions ) ما می‌توانیم توابعی تودرتو داشته باشیم که عملیات خاصی را برای ما انجام دهند؛‌ func NestedFunctions(_ name: String) -> (String) -> String{ func lastNameOne(last_name_one: String) -> String { return "Last Name is One : \(last_name_one)"} func lastNameTwo(last_name_two: String) -> String { return "Last Name is Two : \(last_name_two)"} func Error(error: String ) -> String {return "Error is : \(error)"} if name == "Mohammad" { return lastNameOne } else if name == "Mehdi" { return lastNameTwo } else { return Error } } print(NestedFunctions("Mohammad")("Abasi")) // or var _function = NestedFunctions("Mehdi") print(_function("Abasi")) let funct = NestedFunctions("Hamed") print(funct("This is not name!")) در قطعه کد بالا، ابتدا ما یک تابع به NestedFunctions تعریف کرده و سپس یک پارامتر ( name : String ـ ) ایجاد کردیم. نوع‌های برگشتی را شاید تعجب کرده باشید! اما کاملا ساده هستند! اولین آن‌ها ( ( String ) <- ) اصلا نوع برگشتی نیست! بلکه این نوع‌ها، برای پارامتر‌های توابع داخلی هستند ( lastNameOne, lastNameTwo ) شما می‌توانید تا بی‌نهایت پارامتر اضافه کنید ( دقت کنید به همان تعداد پارامتر که در تابع خارجی برای توابع داخلی اضافه می‌کنید، به همان تعداد پارامتر هم باید برای خود توابع بنویسید ).به همین خاطر هم است که با درون پرانتز این نوع داده‌ها را نوشتیم تا با نوع برگشتی، اختلالی ایجاد نکند. و در نهایت نوع برگشتی ( String <- ) برای برگرداندن مقدار برگشتی توابع داخلی است! در واقع کار تابع NestedFunctions برگرداندن یک تابع است! در بدنه‌ی تابع NestedFunction دو تابع داخلی تعریف کردیم ( lastNameOne, lastNameTwo ) که البته بسته به کار شما، تابع می‌تواند در یک خط یا چند خط نوشته شود. اگر به دقت نگاه کرده باشید می‌بییند که توابع داخلی فقط یک پارامتر دارند که یکسان با پارامتر در تابع NestedFunctions است. بنابراین اگر خواستید پارامتر‌های بیشتری اضافه کنید، باید هر دو پارامتر‌ها را تغییر دهید! به این صورت؛ func NestedFunctions(_ name: String) -> (String,Int) -> String{ func lastNameOne(last_name_one: String, age: Int) -> String { return "Last Name is One : \(last_name_one) and Age is : \(age)"} func lastNameTwo(last_name_two: String, age: Int) -> String { return "Last Name is Two : \(last_name_two) and Age is : \(age)"} func Error(error: String, status_code: Int) -> String {return "Error is : \(error) and Status of code : \(status_code)"} if name == "Mohammad" { return lastNameOne } else if name == "Mehdi" { return lastNameTwo } else { return Error } } print(NestedFunctions("Mohammad")("Abasi", 20)) // or var function = NestedFunctions("Mehdi") print(function("Abasi", 18)) let funct = NestedFunctions("Hamed") print(funct("This is not name!",440)) /* Last Name is One : Abasi and Age is : 20 Last Name is Two : Abasi and Age is : 18 Error is : This is not name! and Status of code : 440 */ همان‌طور که مشاهده کردید، می‌توانید پارامتر‌های مورد‌نیازتان را هماهنگ با پارامتر‌های توابع داخلی تعریف کنید. مورد بعد نوع برگشتی تابع داخلی است که کاملا باید با نوع برگشتی مشخص شده در تابع خارجی مطابقت داشته باشد و در نهایت در بدنه‌ی تابع داخلی می‌توانید دستورات خود را بنویسید و در آخر داده‌‌یم مورد‌نظر را با استفاده از return برگردانید. در خطوط بعد دستورات شرطی را خواهیم داشت که بر اساس پارامتر name کار می‌کنند! اگر مقدار name برابر با رشته‌ی Mohammad باشد، تابع lastNameOne را برمی‌گردانیم و اگر مقدار آن برابر با رشته‌ی Mehdi باشد، تابع lastNameTwo را برمی‌گردانیم. اگر مقدار name با هیچ‌کدام از رشته‌های گفته شده، تطابق نداشته باشد، تابع Error برگشت داده می‌شود. دقت کنید که برای برگشت یک تابع، فقط نام آن را می‌آوریم و نیازی به پرانتز نیست. برای فراخوانی هم شما به دو صورت می‌توانید عمل کنید؛ از آنجا که مقداری که NestedFunctions برمی‌گرداند، یک تابع است، پس باید به تابع داخلی هم دسترسی داشته باشیم! به دو روش می‌توانیم این‌کار را انجام بدهیم؛ روش اول به این شکل است؛ print(NestedFunctions("Mohammad")("Abasi", 20)) یعنی همزمان با دادن پارامتر به NestedFunctions، همان لحظه به صورتی که می‌بینید، در بین دو پرانتز () آرگومانت‌های تابع داخلی را هم بدهید (Abasi", 20" ). یا یک متغییر تعریف کرده و تابع داخلی را از NestedFunctions گرفته و به متغییر انتساب دهید؛ var function = NestedFunctions("Mehdi") print(function("Abasi", 18)) شما به این دو صورت می‌توانید به توابع داخلی خودتان دسترسی داشته باشید. امیدواریم که این جلسه مورد رضایت شما قرار گرفته باشد.
  15. علی رضا نیگ پی

    در این قسمت میخوایم تلاش کنیم کودا (cuda) رو بر روی اوبونتو (Ubuntu) نصب کنیم نسخه کودا که نصب کردم ۱۰.۰ هست و همچنین اوبونتو ۱۸.۰۴ ولی بعید میدونم فرق خاصی داشته باشه نصب بقیه ورژن های چه کودا چه اوبونتو. اول از همه به قسمت Software & Updates برید و پنجره Additional Drivers رو انتخاب کنید و کارت گرافیک خودتون رو نصب کنید بعد از اینکه نصب شد سیستم رو ریبوت کنید یا در ترمینال خط زیر و بزنید: sudo reboot در مرحله بعد باید ملزومات کودا رو نصب کنید و از اونجایی که وقتی فایل deb رو نصب کنید این وابستگی ها خودشون نصب می‌شن ماهم دستی خودمون نصب می‌کنیم چون از deb استفاده نمی‌کنیم! پس: sudo apt-get install freeglut3 freeglut3-dev libxi-dev libxmu-dev این خط پکیج های لازم رو برای کتابخانه های GL - GLU - Xi - Xmu و چندین تای دیگه رو میگیره که بعدا به عنوان وابستگی نصب میشن. حالا به سایت Cuda Zone و دانلود رو بزنید بعدش تو صفحه باز شده Architecture و Distribution و Version روخودتون بر اساس لینوکسی که دارید پیش ببرید و برای دریافت اطلاعات در اوبونتو میتونید از lsb_release -a استفاده کنید. در قسمت نوع نصاب‌ (installer type) گزینه runfile (local) رو انتخاب کنید و دوتا فایل رو که یکی در حد چندین مگابایت و دیگری چیزی در حدود ۲ گیگابایت هست رو دانلود کنید. حالا نوبت نصب cuda-toolkit و نمونه ها (samples): sudo sh cuda_10.0.130_410.48_linux.run اینو که زدید بعدش سوالاتی مثل سوالات زیر پرسیده میشه ازتون و دقت کنید مثل نه دقیقا برای همین سوالات و بخونید و ببینید کدوم پرسیده میشه و کدوم پرسیده نمیشه البته اگر کودا ۱۰ نصب می‌کنید دقیقا همین سوالا پرسیده میشه یا اینکه خود شرکت عوض کرده خلاصه دقت کنید تا در زندگی پیشرفت کنید!! Install NVIDIA Accelerated Graphics Driver for Linux-x86_64 396.26? (y)es/(n)o/(q)uit: n Install the CUDA 10.0 Toolkit? (y)es/(n)o/(q)uit: y Enter Toolkit Location [ default is /usr/local/cuda-10.0 ]: Do you want to install a symbolic link at /usr/local/cuda? (y)es/(n)o/(q)uit: y Install the CUDA 10.0 Samples? (y)es/(n)o/(q)uit: y Enter CUDA Samples Location [ default is /home/kinghorn ]: /usr/local/cuda-10.0 برای بعضی ها شاید سوال: You are attempting to install on an unsupported configuration. Do you wish to continue? (y)es/(n)o [ default is no ]: y هم پرسیده بشه که جوابش و y بدید که بگذرید. سوال اول داره اجازه میگیره که درایور کارت گرافیک رو نصب کنه: از اونجایی که بالا ما نصبش کردیم حتما حتما حتما بزنید n و تقریبا مهمترین قسمت این نصب هم همینه که اینو بزنید n. سوال دوم داره اجازه نسخه کودا تولکیت (cuda toolkit) رو ازتون میگیره که این برمیگرده به نسخه ای که دانلود کردید و برای مثلا من نسخه ۱۰.۰ بود. سوال سوم مکان رو ازتون میخواد که شما همون پیش فرض رو بذارید یعنی دکمه Enter رو بزنید تا برسید به سوال چهارم. سوال چهارم هم اجازه برای نصب symbolic در مکانی که در سوال بالا بهش دادید و سوال پنجم هم نصب نمونه هاست و مکانش رو در سوال ششم بصورتی که نوشتم با این تفاوت که بجای فایل کودا فایل کودا نسخه خودتون و قرار بدید. Enter CUDA Samples Location [ default is /home/kinghorn ]: /usr/local/cuda-x.x بحای x.x نسخه خودتون رو بنویسید. بعضی اوقات خطاهایی بدلیل کارت گرافیک و اینا میاد که خودش چند خط بعد نوشته جلو کلمه try چه چیزی بزنید و اگر اون ارور هارو دریافت کردید اون خط رو بزنید و ان شالله نصب میشه بعد از این کارا اون فایل چند مگابایتی رو حالا اجرا کنید sudo sh cuda_10.0.130.1_linux.run اسمش هم دیگه به چیزی که دارید نصب میکنید بستگی داره و خلاصش اینه که فایلی که اسم کوتاه داره همون چند مگابایتیه هست و اون فایل که اسم بلندی داره ۲ گیگابایتیه هستش. حالا نوبت مقدار دهی Environment Variables: در اینجا مقدار Environment هارو با این فرض مقدار دهی میکنیم که چندین کاربر (user) داریم. البته سیستم خود من تک کاربره هست ولی همچنان با این متد رفتم شما هم میتونید برید و مشکلی نداره sudo nano /etc/profile.d/cuda.sh با این خط به فایل در مسیر داده شده به اسم cuda.sh می‌سازیم و بعد از اون با دستور nano باز میکنیم و متن رو داخلش قرار میدیم: export PATH=$PATH:/usr/local/cuda/bin export CUDADIR=/usr/local/cuda بعد از اون فایل رو ذخیره می‌کنیم یعنی کلید ها Ctrl + x سپس ‌y و بعد از اون Enter رو میزنیم و ذخیره میشه فایل بعد از این خط زیر و اجرا میکنیم تا فایل مورد نظر باز بشه sudo nano /etc/ld.so.conf.d/cuda.conf حالا خط زیر و انتقال میدیم داخلش و بعد همون دکمه های قبلی رو میزنیم تا فایل ذخیره بشه /usr/local/cuda/lib64 بعد از این کار دستور زیر و اجرا میکنیم و بعد از دستور زیر یه بار log out می‌کنیم تا محتویات اجرا شن و بعدش دیگه دسترسی دارید از شل (shell) برای استفاده از دستور ها رو دارید sudo ldconfig حالا اگر این و انجام دادید که خوشا به سعادتتون و خسته نباشید اگرم منتظرید که تک کاربرشو بگم بفرما: یه فایل بنام cudax.x-env می‌سازید که اون x.x ورژن کودا شماست مثلا sudo nano cuda10.0-env بعدشم سه خط و زیر و بهش اضافه می‌کنیم و با همون دکمه های قبلی ذخیرش می‌کنیم export PATH=$PATH:/usr/local/cuda-10.0/bin export CUDADIR=/usr/local/cuda-10.0 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-10.0/lib64 تو این سه خط میبینید نسخه کودا هم اضاف کردم در صورتی که میتونستم فقط کودا رو بنویسیم ولی اینطوری شما میتونید چندین ورژن کودا نصب کنید و هرکدوم Environment Variables خودشو داشته باشه و حالا هر وقت خواستید به سادگی با دستور source cuda-10.0-env حالا با دستور مقدار Environmentها رو قرار میدیم یا به قول معروف set می‌کنیم. البته همچنین فایل رو میتونید هرجایی که خواستید بذارید ولی یادتون باشه که مسیرش رو اگر تغییر دادید موقع زدن خط بالا ادرس رو به source بدید. تموم! به همین راحتی کودا رو نصب کردید ( البته امیدوارم! ) با دستور nvcc --version ورژن کودا نصب شده بهتون نمایش داده می‌شه و با دستور nvidia-smi هم اطلاعات کارت گرافیتون. حالا میخوام یه نمونه کد تست کنیم که جمع دو ماتریس هست: اول با ترمینال و بعدش با vscode انجام میدیم, در ترمینال یه فایل می‌سازیم با پسوند .cu یعنیsudo nano firstcuda.cu و بعدش وقتی باز شد خطوط پایین رو داخلش قرار میدیم #include "cuda_runtime.h" #include "device_launch_parameters.h" #include <stdio.h> __global__ void addKernel(int *c, const int *a, const int *b){ int i = threadIdx.x; c[i] = a[i] + b[i]; } int main(){ const int arraySize = 5; const int a[arraySize] = { 1, 2, 3, 4, 5 }; const int b[arraySize] = { 10, 20, 30, 40, 50 }; int c[arraySize] = { 0 }; cudaError_t cudaStatus; int *dev_a = 0, *dev_b = 0, *dev_c = 0; cudaMalloc((void**)&dev_c, arraySize * sizeof(int)); cudaMalloc((void**)&dev_a, arraySize * sizeof(int)); cudaMalloc((void**)&dev_b, arraySize * sizeof(int)); cudaMemcpy(dev_a, a, arraySize * sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy(dev_b, b, arraySize * sizeof(int), cudaMemcpyHostToDevice); addKernel << <1, arraySize >> >(dev_c, dev_a, dev_b); cudaDeviceSynchronize(); cudaMemcpy(c, dev_c, arraySize * sizeof(int), cudaMemcpyDeviceToHost); printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n", c[0], c[1], c[2], c[3], c[4]); cudaStatus = cudaGetLastError();if (cudaStatus != cudaSuccess)printf("cudaDeviceReset failed!"); return 0; } بعد ذخیره کنید و دستور nvcc firstcuda.cu -o firstcuda رو بزنید. این دستور یعنی با کامپیالر nvcc برنامه firstcuda.cu رو کامپایل کن و خروجی -o بنام firstcuda بده. که خروجی در همون پوشه هست که هموطوری که میدونید بطور پیش فرض همه این ها در قسمت home هستند. حالا خروجی رو اجرا کنید یعنی ./firstcuda و این دستور خروجی رو اجرا میکنه و به شما نمایش میده. خروجی که باید نمایش داده بشه {1,2,3,4,5} + {10,20,30,40,50} = {11,22,33,44,55} حالا بریم سراغ vscode که یکم بیشتر ولی رنگی تره, بقول معروف هرچی رنگین تر باشی سخت تر بدست میای! از قسمت ترمینال (Terminal) گزینه New Terminal رو انتخاب کنید و بعد بنویسید touch test.cu که با این دستور شما فایل test.cu رو میسازید.حالا کد هارو داخلش قرار بدید بعدش میتونید از داخل ترمینال داخل vscode بقیه مراحل و مثل قبل برید و کامپیال کنید و بعدشم اجرا کنید فایل خروجی رو ولی راه قشنگ تر اینه که از قسمت ترمینال گزینه Configure Default Build Task رو انتخاب کنید بعد از اون یه پنجرکی (پنجره کوچک!) باز میشه که روی Create tasks.json file from template کلیک کنید و بعدشم گزینه Others. حالا در فایل جدیدی که ساخته شده با پسوند .json باید خط های زیر رو قرار بدید { "version": "2.0.0", "tasks": [ { "label": "build", "type": "shell", "command": "nvcc", "args": [ "test.cu", "-o", "${workspaceFolderBasename}" ] } ] } خب حالا باید درستش کنیم: اولین چیز لیبل (label) هست که نشون دهنده اسم task میشه و بعد از اون نوع که باید شل (shell) باشه و مهم تر از همه command که نشون دهنده کامپیالرمون هست باید nvcc باشه تا سیستم به طور خودکار کتابخانه هایی که استفاده شده در کد رو تشخیص بده. ارگومان ها اول اسم برنامه هست و بعدی به معنای خروجی هست و سومی نام فایل خروجی که اونی که نوشته شده یعنی هم اسم فایلی که داخلشیم. بعد از سیو کردن (Ctrl + s) وقتی از قسمت ترمینال گزینه Run Task رو میزنیم build نمایش داده میشه و وقتی میزنیمش این کد کامپیایل میشه و در همون فولدر خروجی رو بهمون میده و اگرم دقت کنید یک فایل a.out هم براتون نمایش داده میشه در vscode. حالا کاری میکنیم که وقتی ساخته شد اجرا هم بشه و در ترمینال vscode نمایش داده بشه. اگر دقت کرده باشید خطی جدید به صورت "problemMatcher": [] اضافه شده برای همین در پایین که کل کد هست این خط هارو وارد نکنید: { "version": "2.0.0", "tasks": [ { "label": "build", "type": "shell", "command": "nvcc", "args": [ "test.cu", "-o", "${workspaceFolderBasename}" ], "problemMatcher": [] }, { "label": "run", "type": "shell", "command": "./${workspaceFolderBasename}", "dependsOn": [ "build" ] } ] } این قسمت جدیدی که اضافه شده داره میگه task با نام run اجرا کنه فایل رو (که در قسمت command برابر قرار داده شده این عمل) و باید حتما build اجرا شده باشه و به نوعی مثل همون شرط کار میکنه. اگر بخوایم اسمی خاص و همچنین در فولدر بندی خاصی این عملیات انجام بشه باید آدرس هارو بهشون بدیم: یعنی اول باید به مقدار آرگومان ورودی سوم قسمت build باید آدرس رو کامل وارد کنیم و همچنین در قسمت command بخش run هم همچین کاری کنیم به زبان ساده بخوام بگم میشه: ما یه فولدر بنام Practice داریم که تو اون فولدر فایل های مختلفی داریم که یه فولدر ساختیم بنام Cuda برای پروژه های کودا و همچنین یه فولدر در Cuda ساختیم به نام اسم پروژه ( فرض کنیم test.cu) پس آدرس فایل test.cu میشه ‌Practice/Cuda/Test/test.cu حالا اگر بخوایم اینو اجرا کنیم و خروجی رو ببینیم باید فایل .json رو اینطوری بنویسیم (دقت کنید به بزرگ و کوچیک بودن کلمات!): { "version": "2.0.0", "tasks": [ { "label": "build", "type": "shell", "command": "nvcc", "args": [ "Practice/Cuda/Test/test.cu", "-o", "Practice/Cuda/Test/test" ], "problemMatcher": [] }, { "label": "run", "type": "shell", "command": "./Practice/Cuda/Test/test", "dependsOn": [ "build" ], "problemMatcher": [] } ] } الان یه فایل ورودی با اون آدرس و با پسوند .cu به کامپایلر داده میشه و بعدش فایلی با نام test که نامی دلخواه هست و هرچی خواستید میتونید بذارید ساخته میشه به عنوان خروجی و در قسمت run میاد فایل رو با آدرس دقیقا در قسمت command میگیره و اجراش میکنه (دقیقا همون کاری که در ترمینال اوبونتو میکنید اینجا اومدید دکمه ایش کردید). حالا از قسمت Run Task روی run کلیک میکنیم تا کامپیال و هم چنین اجرا بشه. فایل نهایی شما (بعد از اجرا کردن) باید تقریبا به شکل زیر باشه (تقریبا برای این میگم شاید فردی بخواد وابستگی هارو بیشتر کنه و یا اسم برنامه چیز دیگه ای باشه و ...‌) { "version": "2.0.0", "tasks": [ { "label": "build", "type": "shell", "command": "nvcc", "args": [ "test.cu", "-o", "${workspaceFolderBasename}" ], "problemMatcher": [] }, { "label": "run", "type": "shell", "command": "./${workspaceFolderBasename}", "dependsOn": [ "build" ], "problemMatcher": [] } ] } خروجی زیبا رو در ترمینال میتونید تماشا کنید و لذت ببریدخسته نباشید ان شالله همیشه پیروز و موفق باشید :] منبع اصلی هم مقاله Dr Donald Kinghorn و همچنین چندین (خیلی زیاد) ویدیو و مقاله و پاسخ جواب های دیگه تا تونستم بالاخره نصب کنم و بدون مشکل استفاده کنم.
  16. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه نُهم مواردی که در این جلسه یاد خواهید گرفت: تاپل‌ها ( Tuples ) تاپِل‌ها چیستند؟‌ تاپل‌ها گروهی از داده‌ها هستند که از داده‌های مختلفی تشکیل شدند و درواقع ترکیبی از مقادیر هستند! در این نوع،‌ برای دسترسی به مقادیر، از کلید/مقدار ( key/value ) استفاده می‌کنیم. این نوع از مجموعه‌ها، می‌توانند هر داده‌ای را در خود ذخیره کنند و نیازی به تعریف نوع داده برای آن‌ها نیست. به زبان ساده؛ تاپل‌ها از این‌که شما بخواهید برای هر نوع داده‌ای یک کلاس جداگانه تعیین کنید (‌ مانند Set )، جلوگیری می‌کند و مجموعه‌ای از داده‌های مختلف را برای شما فقط در یک متغییر ذخیره می‌کند و در ادامه می‌توانید از آن استفاده کنید. تعریف یک تاپل برای این‌کار، ابتدا یک متغییر تعریف کرده ( کلمات کلید let, var ) یک نام انتخاب کرده و سپس در بین دو پرانتز () داده‌های خود را با کاما ( , ) از هم جدا می‌کنید؛ let ـfamily = ("Mohammad", 20, 1378, "Esfahan-Felavarjan") همچنین شما می‌توانید بخشی از داده‌‌های یک تاپل را در یک متغییر دیگر ذخیره کنید و یا به اصطلاح بُرش بزنید! به این مثال دقت کنید؛ let ـfamily = ("Mohammad", 20, 1378, "Esfahan-Felavarjan") let (first_name, age, birthday, _) = family print("FirstName is : \(ـfirst_name)") // FirstName is : Mohammad اگر دقت کرده باشید، در آخرین تعریف یک متغییر، بجای نام ما از آندِرلاین ( ـ ) استفاده کردیم! دلیل آن هم این است که اگر مقداری را نخواهیم، از این علامت استفاده می‌کنیم. به عنوان مثال؛‌ مقدار رشته‌ای Esfahan-Felavarjan را نادیده گرفتیم و با علامت ( ـ ) گفتیم که مقدار آخر را نادیده بگیر! اگر این‌کار را نکنید، خروجی به شما نمایش داده نخواهد شد و با خطای کامپایلر روبرو خواهید شد. نکته دقت داشته باشید که شما اگر بخواهید بخشی از داده‌های تاپل را داشته باشید و در یک متغییر ذخیره کنید، دو راه بیشتر ندارید؛ یا این‌که همه‌ی داده‌ها را دریافت کنید، مانند مثال زیر؛ let ـfamily = ("Mohammad", 20, 1378, "Esfahan-Felavarjan") let (first_name, age, birthday, address) = family print("FirstName is : \(ـfirst_name)") // FirstName is : Mohammad یا این‌که با علامت ( ـ )، هر کدام از داد‌ه‌ها را که مد‌نظر شما نبود، نادیده گرفته و در نتیجه ذخیره نکنید؛ let ـfamily = ("Mohammad", 20, 1378, "Esfahan-Felavarjan") let (first_name, ـ , birthday, ـ) = family print("FirstName is : \(ـfirst_name)") // FirstName is : Mohammad اما دقت داشته باشید که شما به هیچ‌وجه نمی‌توانید با اختیار خود یکی از مقادیر را نادیده گرفته ( متغییر برای آن در نظر نگیرید ) و یا این‌که از علامت گفته شده، استفاده نکنید! شما به هر حال باید از یکی از این دو روش استفاده کنید. دسترسی به اعضای با استفاده از شماره موقعیت ( Index )‌ این‌کار به صورتی جذاب انجام می‌شود! در این نوع از دسترسی، براکت (‌ [] ) خبری نخواهد بود و شما فقط با آوردن موقعیت ( Index ) می‌توانید به مقدار تاپل دسترسی داشته باشید؛ let ـfamily = ("Mohammad", 20, 1378, "Esfahan-Felavarjan") print("FirstName is : \(_family.0)") // FirstName is : Mohammad print("Age is : \(_family.1)") // Age is : 20 همان‌طور که مشاهده کردید، با آوردن شماره موقعیت، می‌توانید به مقدار آن دسترسی داشته باشید. دسترسی به اعضای با استفاده از نام شما همچنین می‌توانید با استفاده از نامی که در ابتدای تعریف یک تاپل استفاده می‌کنید، با همان نام، به اعضای آن دسترسی داشته باشید؛ let ـfamily = (first_name: "Mohammad", age: 20, birthday: 1378, address: "Esfahan-Felavarjan") print("FirstName is : \(_family.first_name)") // FirstName is : Mohammad print("Age is : \(_family.age)") // Age is : 20 همان‌طور که مشاهده کردید، شما می‌توانید برای مقادیر خود نام هم تعیین کنید و در ادامه با استفاده از همان نام، به مقدار آن دسترسی داشته باشید ( همانند دیکشنری ). بنابر‌این اگر در جایی از پروژه‌ی خود نیاز به مقدار‌های مختلفی داشتید و نخواستید که از کلاس‌ها و نوع‌های داده‌ی متفاوت استفاده کنید، می‌توانید از تاپل‌ها برای این منظور استفاده کنید. امیدواریم که از این جلسه مورد رضایت شما قرار گرفته باشد.
  17. به نام خدا با سلام خدمت دوستان گرامی. از آنجایی که کامپایل هر کتابخانه مرتبط با زبان C++ در ویندوز نکات و فوت و فن خاص خود را دارد لذا تصمیم بر آن شد تا در اینجا به نحوه کامپایل کتابخانه Curl در این سیستم عامل بپردازیم. مشخصات کلی کامپایلر و کتابخانه به شرح زیر می‌باشد: Curl: 7.68.0 Microsoft Build Tools: 15.9.18 Compiler and OS Architectures: x64 ابتدا به این سایت رفته و کد منبع Curl را دریافت نمایید، دقت کنید که باینری کتابخانه Curl برای ویندوز موجود است ولی با کامپایلر MinGW برای ویندوز کامپایل شده که مطلوب ما نمیباشد. به دلیل اعلام نویسندگان Curl در رابطه با نگه‌داری ضعیف در پشتیبانی از CMake لذا به شکل مستقیم از کنسول مایکروسافت و Makefile سازگار با آن یعنی Makefile.vc استفاده خواهد شد. پس از دریافت و استخراج محتویات، می‌بایست کنسولx64 Native Tools Command Prompt for VS 2017 را باز کرده و دستورات زیر را مطابق شکل در آن وارد می‌کنیم: F: cd F:\curl-7.68.0\winbuild که دستور اول برای تغییر درایور و دستور دوم نشانی محل استخراج کتابخانه Curl روی سیستم نگارنده مطلب می‌باشد. سپس دستور زیر را وارد می‌کنیم: nmake /f Makefile.vc mode=dll که در دستور بالا /f مشخص کننده نشانی makefileمورد نظر و modeمشخص کننده نحوه کامپایل کتابخانه به شکل ایستا یا پویا را شامل می‌شود که در اینجا کتابخانه به شکل پویا کامپایل می‌شود. چنانچه مایل باشیم کتابخانه به شکل ایستا کامپایل شود می‌بایست دستور زیر را وارد کنیم: nmake /f Makefile.vc mode=static پس از ورود دستور بالا کتابخانه در مسیر F:\curl-7.68.0\ پوشه‌ای با نام buildsساخته و فایل‌های حاصل از کامپایل را در آن ذخیره می‌کند که در تصویر زیر نتیجه نهایی کامپایل آورده شده است. حال یک پروژه ساده از نوع Plain C++ Application در Qt Creator به منظور آزمایش کامپایل صحیح کتابخانه Curl ایجاد می‌کنیم. قطعه کد زیر را در فایل main.cpp وارد می‌کنیم: #include <curl\curl.h> int main() { CURL *curl; curl = curl_easy_init(); curl_easy_cleanup(curl); return 0; } و درنهایت در فایل CMake پروژه دستورات زیر را وارد می‌کنیم: cmake_minimum_required(VERSION 3.5) project(Curl LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(Curl main.cpp) target_include_directories(Curl PRIVATE "F:/curl-7.68.0/builds/libcurl-vc-x64-release-dll-ipv6-sspi-winssl/include") target_link_libraries(Curl PRIVATE "F:/curl-7.68.0/builds/libcurl-vc-x64-release-dll-ipv6-sspi-winssl/lib/libcurl.lib") در گام آخر نیاز است تا فایل dll حاصل از کامپایل کتابخانه را در کنار فایل اجرایی پروژه قرار داده تا برنامه بدون مشکل اجرا شود این فایل در شاخه bin واقع در پوشه builds محل کامپایل کتابخانه موجود می‌باشد. چنانچه کتابخانه را به شکل ایستا کامپایل کرده باشیم نیاز است تا ماکرو CURL_STATICLIB را قبل از ورود هرگونه فایل سرآیند کتابخانه Curl مطابق زیر تعریف کنیم: #define CURL_STATICLIB متن کامل کد با رعایت نکته گفته شده چنین می‌باشد: #define CURL_STATICLIB #include <curl\curl.h> int main() { CURL *curl; curl = curl_easy_init(); curl_easy_cleanup(curl); return 0; } چنانچه مشکل یا ایرادی در نوشته بالا ملاحظه نمودید، نگارنده مطلب را بی خبر نگذارید. سپاس فراوان.
  18. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه هشتم مواردی که در این جلسه یاد خواهید گرفت: دیکشنری ( Dictionary ) دیشکنری‌ها چیستند؟ به طور ساده و خلاصه؛ یکی از نوع‌های مجموعه‌ است که مقادیر یا همان داده‌ها را به صورت کلید/مقدار ( key/value ) ذخیره می‌کند و همچنین نامرتب است و داده‌های تکراری را فقط یک‌بار مورد ذخیره! این‌کار به شدت می‌تواند به برنامه‌نویس کمک کند تا راحت‌تر، بهتر و سریع‌تر به داده‌های خود از طریق کلید ( key ) دسترسی داشته باشد و همچنین کُدی خوانا‌تر داشته باشد. برای این‌که این موضوع برای شما عزیزان شفاف‌تر شود و بهتر بتوانید این موضوع را درک کنید، شروع به ایجاد دیکشنری خودمان خواهیم کرد و در ادامه، توضیحات لازم را ارائه خواهیم داد. برای ایجاد یک دیکشنری، به چهار صورت می‌توانیم عمل کنیم؛ استفاده از کلاس Dictionary و سپاس در بین دو علامت <> کلید و مقدار خود را قرار می‌دهیم <Dictionary<Key, Value که البته باید در نظر داشته باشید، Key و Value نوع دادهایی هستند که باید قرار گیرند و بعد‌ا مقدار‌دهی بر اساس نوع‌های داده خواهد بود نه مستقیما مقدادهی کنید! مانند؛ <Dictionary<String, Double تعریف یک متغییر و سپس استفاده از الگوی؛‌ ()[String : String ] استفاده از روش دوم با این تفاوت که شما می‌بایست، دیکشنری خود را همانند تعریف یک متغییر به صورت صریح بنویسید! شما می‌توانید فقط از [] به صورت مستقیم استفاده کنید و داده‌ها را اضافه کنید، مانند؛ ["var products = ["www.fanoox.com : "MacBook Pro از اولین مورد شروع خواهیم کرد، پس به این شکل عمل می‌کنیم؛ var users_and_passwords= Dictionary<String, String>() users_and_passwords["Mohammad"] = "!#$#@%#$!#@$" users_and_passwords["Ali"] = ")+_)(*&^^%" users_and_passwords = ["Ali" : "_)(*&^%^&*", "Hamed" : "123456789%$#@!#$^%!$1009876!!$$%!$54321"] print(users_and_passwords) /* ["Ali" : "_)(*&^%^&*", "Hamed" : "123456789%$#@!#$^%!$1009876!!$$%!$54321" ] */ همان‌طور که مشاهده می‌کنید، یک متغییر تعریف کرده و سپس از کلاس Dictionary استفاده کردیم که در بین دو علامت <> ( بزرگ‌تری و کوچک‌تری ) نوع‌هایی که می‌خواهیم از آن‌ها در برنامه‌مان استفاده کنیم، به کار بردیم. شما می‌توانید هم به تنهایی و هم گروهی، مقداردهی را شروع کنید! در قطعه‌کد بالا می‌بینید که خط ۲ و ۳، به صورت تَکی و خط ۴ به صورت گروهی مقداردهی شده است و در نهایت در خط ۵ آن را چاپ می‌کنیم. توجه داشته باشید که اگر در حالت گروهی بخواهید مقدادهی کنید،‌ مقادیر قبلی از بین خواهند رفت! در حالی که اگر شما به صورت تکی مقداردهی کنید، این اتفاق نخواهد افتاد. نکته اگر به صورت تَکی بخواهید مقداردهی کنید، هیچ اتفاقی برای مقادیر دیگر نخواهد افتاد! اما اگر گروهی مقداردهی کنید، داده‌های قبلی به کل پاک و داده‌های جدید، قرار خواهند گرفت. دومین نوع تعریف بدون استفاده از کلاس است؛ var languages_and_framework = [String : String]() languages_and_framework["C++"] = "Qt Creator " languages_and_framework["PHP"] = "Laravel" print(languages_and_framework) /* ["C++" : "Qt Creator ", "PHP" : "Laravel"] */ در سومین حالت تعریف به این شکل عمل خواهیم کرد؛‌ var languages_and_framework : [String : String] = ["C++" : "Qt Creator", "Javascript" : "Angular.js"] print(languages_and_framework) /* ["C++" : "Qt Creator", "Javascript" : "Angular.js"] */ و در نهایت در حالت چهارم اگر بخواهیم دیکشنری خودمان را تعریف کنیم، به این صورت عمل می‌کنیم؛‌ var languages_and_framework = ["C++" : "QT Creator", "PHP" : "Laravel"] print(languages_and_framework) /* ["PHP" : "Laravel", "C++" : "Qt Creator"] نکته در تمامی این مثال‌ها، وقتی داده‌ها را نمایش می‌دهیم، به صورت نامرتب نمایش داده خواهند شد! چرا که همان ابتدا گفتیم، دیکشنری‌ها، نامرتب هستند و این مورد در خروجی داده‌ها هم صِدق می‌کند. نحوه‌ی دسترسی به داده‌های ما می‌توانیم با استفاده کلید ( Key ) به داده‌های مرتبط با آن دسترسی داشته باشیم! به این صورت که شما نام کلیدی را که از قبل انتخاب کردید، در بین دو براکت به همراه اسم دیکشنری خود آورده و سپس علامت تعجب (‌ ! ) را نوشته تا بتوانید مقدار مورد نظر را در دسترس داشته باشید! حتمالا با خود فکر می‌کنید که چرا علامت تعجب؟! اگر از جلسات قبل به خاطر داشته باشید، درمورد متغییر‌های آپشِنال ( Optionals ) صحبت کردیم و گفتیم که برای دسترسی به مقادیر این نوع متغییر‌ها باید از علامت ! استفاده کنیم. در دیکشنری‌ها هم همین موضوع برقرار است و شما برای استفاده از داده‌های آن باید آن‌ها را از حالت آپشنال خارج کنید. به این مثال دقت کنید؛ let _name_and_age : [String : UInt] = ["Mohammad" : 20, "Hamed" : 30] print(_name_and_age["Mohammad"]!) // 20 اگر علامت ! را ننویسید، با خطای کامپایلر مواجه خواهید شد. به همین صورت تا هر چند مقدار را که می‌خواهید می‌توانید به آن دسترسی داشته باشید. دقت کنید که اگر بخواهید یک مقدار خاص را در یک متغییر جداگانه ذخیره کنید، نیازی به علامت ! نخواهید داشت؛ let _name_and_age : [String : UInt] = ["Mohammad" : 20, "Hamed" : 30] let _age = _name_and_age["Mohammad"] print(_age["Mohammad"]!) // 20 اگر بخواهید متغییری که جداگانه تعریف کردید، نمایش دهید؛ آن موقع باید حتما از علامت تعجب ( ! ) استفاده کنید. اضافه کردن یک مقدار برای اضافه کردن یک مقدار، به همان صورتی که در بالا ( تکی اضافه کردن ) تعریف کردیم، استفاده می‌کنیم؛‌ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] numbers[40] = 50 print(numbers) /* [20 : 30, 10 : 20, 30 : 40, 40 : 50] */ بروز‌رسانی یک مقدار اگر بخواهیم داده‌های خود را به روز‌رسانی ( Update ) کنیم، کافیست کلید را آورده و در مقابل، مقدار را اضافه کنیم؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] numbers[40] = 50 numbers[10] = 100 print(numbers) /* [20 : 30, 10 : 100, 30 : 40, 40 : 50] */ همان‌طور که مشاهده می‌کنید، کلید ما در این بروز‌رسانی، ۱۰ است است و مقدار ۱۰۰ را به آن اختصاص داده‌ایم! پس دیگر خبری از مقدار ۲۰ نخواهد بود. هم‌چنین شما می‌توانید از متد updateValue از کلاس دیکشنری استفاده کنید. این متد، علاوه بر این‌که مقدار فعلی از یک کلید را برمی‌گرداند، همزمان، مقدار جدید را در همان کلید قرار خواهد داد. این متد دو آرگومان ( Argumant ) دریافت می‌کند که به شرح زیر است؛ اولین آرگومان، مقدار جدید است. دومین آرگومان، کلید است. مقدار برگشتی که از این متد، آپشنال خواهد بود! به این مثال دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] numbers[40] = 50 numbers[10] = 100 if old_number = numbers.updateValue(200, forkey: 20){ print("Old the number : \(old_number)") // Old the number : 30 } print(numbers) /* [20 : 200, 10 : 100, 30 : 40, 40 : 50] */ در قطعه کد بالا مشاهده می‌کنید که از یک شرط استفاده کردیم و مقدار برگشتی از متد updateValue را به متغییر old_number اختصاص داده‌ایم. این مقدار از کلید آن یعنی forkey: 20 گرفته شده است. هنگام چاپ مقدار old_number نیاز به قرار دادن علامت تعجب ( ! ) نیست! این‌کار در همان‌ابتدا در شرط انجام شده است. و در نهایت، در خروجی نهایی کل دیکشنری همان‌طور که می‌بینید، مقدار ۲۰۰ به کلید ۲۰ اختصاص داده شده است. اگر کلید که شما در آرگومان دوم داده باشید، وجود نداشته باشد، پیغام Not Exit و دیکشنری نمایش داده می‌شود، بدون این‌که مقداری حذف شده باشد‌. حذف کلید و مقدار اگر بخواهید کلید و مقدار خود را در حذف کنید، از کلمه‌ی کلید nil می‌توانید استفاده کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] numbers[30] = nil print(numbers) /* [20 : 30, 10 : 20] */ مشاهده می‌کنید که کلید ۳۰ و مقدار ۴۰ هر دو حذف شدند. شاید با خودتان بگوید چرا کلید هم حذف شده است؟ به این دلیل که اگر مقداری برای کلید نباشد، خود کلید هم حذف خواهد شد؛ چون مقداری ندارد. شما می‌توانید از متد removeValue نیز استفاده کنید! این متد یک آرگومان گرفته و کلید/مقدار را حذف می‌کند. آرگومانی که این متد دریافت می‌کند، کلید است و مقدار برگشتی آن، مقدار حذف‌شده و به صورت آپشنال است ( اگر کلید که شما در آرگومان دوم داده باشید، وجود نداشته باشد، پیغام Not Exit به همراه خود دیکشنری نمایش داده می‌شود. ) به این مثال دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] if remove_value = numbers.removeValue(forkey: 10){ print("Removed value : \(remove_value)") // Removed value : 20 } print(numbers) /* [20 : 30, 30 : 40] */ همان‌طور که مشاهده می‌کنید، مقدار ۲۰ نمایش داده شده که درواقع مقدار حذف شده است. و در آخر هم کل دیکشنری را نمایش می‌دهیم که کلید ۱۰ و مقدار ۲۰ حذف شده‌اند. همچنین شما می‌توانید با استفاده از متد removeAll همه‌ی داده‌های دیکشنری را حذف کنید! دقت کنید که فقط داده‌های آن حذف خواهند شد نه خود دیکشنری! به این مثال دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] numbers.removeAll() print(numbers) // [:] همان‌طور که مشاهده کردید، فقط داده‌ها حذف شدند و خود دیکشنری هنوز در برنامه‌ی ما وجود دارد؛ بنابراین در ادامه می‌توانید داده‌های خود را اضافه کنید. گردش در دیکشنری ( Iterating over a dictionary ) شما می‌توانید با استفاده از حلقه‌ی for کلید‌ها و مقدار‌ها را دریافت کنید! هر کدام از این آیتم‌ها ( کلید/مقدار ) یک تاپِل هستند. به این مثال دقت کنید تا بهتر این موضوع را متوجه شوید؛‌ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] for (key, value) in numbers{ print("Key : \(key) and value : \(value)") } /* Key : 10 and value : 20 Key : 20 and value : 30 Key : 30 and value : 40 */ در قطعه کد بالا شما در حلقه‌ی for در بین دو پرانتز () به دلخواه دو نام انتخاب کرده که نشان از کلید و مقدار است و در نهایت کلمه‌ی کلید in را نوشته و دیکشنری را می‌نویسید. به دست آوردن کلید‌ها اگر بخواهید فقط و فقط کلید‌های یک دیکشنری را به دست آورید، می‌توانید با استفاده از ویژگی ( Properties‌ ) به نام keys در حلقه‌ی for یا به تنهایی استفاده کنید. به این مثال دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] for key in numbers.keys{ print("Key : \(key)") } /* Key : 10 Key : 20 Key : 30 */ // Or print("Keys : \(numbers.keys)") // Output: Keys : [10,20,30] به دست آوردن مقدار‌ها در مقابل شما می‌توانید با استفاده از ویژگی values فقط و فقط به مقادیر دسترسی داشته باشید. به این مثال دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] for value in numbers.values{ print("Value : \(value)") } /* Value : 20 Value : 30 Value : 40 */ // Or print("Values : \(numbers.values)") // Output: Values : [20,30,40] مرتب‌سازی همان‌طور که می‌دانید و همان‌ ابتدا هم گفتیم، دیکشنری‌ها نوعی هستند که نامرتب‌‌اند! یعن داده‌ها را به صورت تصادفی ( Random ) نمایش می‌دهند. اما ما در ادامه مثالی را ذکر خواهیم کرد که شما می‌توانید، هم براساس کلید و هم مقدار، اقدام به مرتب‌سازی داده‌ها کنید. مرتب‌سازی بر اساس مقدار در این حالت ما با استفاده از ویژگی values و سپس متد sorted که همراه خودش دارد، براساس مقدار فقط داده‌ها را مرتب می‌کنیم؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] for sorted_by_value in numbers.values.sorted{ print("Sorted by value : \(sorted_by_value)") } /* Sorted by value : 20 Sorted by value : 30 Sorted by value : 40 */ نکته استفاده از دو حالت بالا صحیح است و اگر شما دو ویژگی values و keys را نیاورید، و فقط متد sorted را به همراه نام دیکشنری بنویسید، اشتباه ‌خواهد بود و از طرف کامپایلر خطا دریافت می‌کنید. به مثال زیر که اشتباه است دقت کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] print(numbers.sorted()) // Error همان‌طور که مشاهده می‌کنید، استفاده از متد بدون ذکر یک از دو ویژگی values و keys از نظر کامپایلر بی‌معنی است و در نتیجه خطا را صادر می‌کند. دسترسی به تعدا اعضای دیکشنری برای این‌که بتوانید به تعداد اعضای یک دیکشنری دسترسی داشته باشید، از ویژگی count‌ می‌توانید استفاده کنید؛ var numbers : [Int : Int] = [10 : 20, 20 : 30, 30 : 40] print("Count the numbers : \(numbers.count)") // Count the numbers : 3 دقت کنید که منظور از تعدا اعضا، هم کلید و هم مقدار نیست! بلکه یعنی یک کلید و یک مقدار می‌شود یک عضو! در قطعه کد بالا هم مشاهده می‌کنید که ما سه عضو داریم که در خروجی نمایش داده شده است. بررسی خالی بودن یا نبودن دیکشنری با استفاده از ویژگی isEmpty شما می‌توانید، بررسی کنید که آیا دیکشنریتان خالی است یا نه؟! به این مثال دقت کنید؛ var numbers : [Int : Int] = [:] if numbers.isEmpty{ print("Dictionary is empty!") // Yes, it is Empty } else { print("Dictionary is not empty!") } با استفاده از [:] می‌توانید در همان ابتدا اعلام کنید که دیکشنری شما خالی است. همان‌طور که در خروجی هم می‌بینید، خروجی برابر است با Dictionary is empty است. امیدواریم که این جلسه مورد رضایت شما قرار گرفته باشد.
  19. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه هفتم مواردی که در این جلسه یاد خواهید گرفت: مجموعه‌ها ( Collections ) مجموعه‌ها در سوئیفت در واقع متغییر‌هایی هستند که بیش‌از یک مقدار را نگهداری می‌کنند و هر کدام، هم طبق عملکردی که دارد، داده‌ها را ذخیره و در اختیار برنامه‌نویس می‌گذارد. منظور از هر کدام، ۴ نوع مجموعه است که در اینجا مشاهده خواهید کرد؛ آرایه‌ ( Array ) سِت‌ ( Set ) دیکشنری ( Dictionary ) تاپِل‌ ( Tuple )‌ در جلسه‌ی قبل، با اولین مجموعه؛ یعنی آرایه‌ها به طور کامل آشنا شدید و در این جلسه با سِت‌ها (‌ Sets )‌ کامل آشنا خواهید شد. به خاطر داشته باشید که هر کدام از این مجموعه‌ها منحصر‌ به فرد عمل کرده و هیچ‌کدام شبیه هم نخواهند بود. سِت‌ها ( Sets ) چیستند؟‌ سِت‌ها کار شما را برای ذخیره کردن داده‌های غیر‌تکراری راحت می‌کنند! یعنی این‌که شما به راحتی و بدون این‌که نگران باشید چه تعداد مقادیر تکراری دارید، می‌توانید به روند توسعه پروژه‌ی خودتان ادامه دهید و مابقی کار را به این یکی نوع از مجموعه‌ها بسپارید. این در صورتی است که شما نمی‌خواهید دائما بررسی کنید که آیا داده‌های تکراری دارید یا نه و با تعریف این نوع، خیالتان آسوده خواهد شد. همچنین داده‌هایی که در این نوع به نمایش درخواهند آمد، به صورت نامنظم هستند! یعنی اینکه اگر شما مقادیر خودتان را به صورت منظم اضافه کرده باشید، خروجی آن چیزی نیست که به صورت منظم خواسته باشید. در واقع می‌توان گفت، Setها؛ داده‌های غیر‌تکراری و نامنظم را در خود نگهداری می‌کنند. برای تعریف یک Set به این صورت عمل خواهیم کرد، می‌توانیم به دو صورت عمل کنیم؛ استفاده از کلاس و تعیین نوع داده استفاده از کلاس بدون تعیین نوع داده به اولین مورد، یعنی استفاده از کلاس و تعیین نوع داده می‌پردازیم؛ پس به این صورت عمل خواهیم کرد؛ var setـvar = Set<String>() // Or any data type همانطور که مشاهده می‌کنید، ابتدا یک متغییر تعریف خواهید کرد و سپس نام کلاس Set را می‌آورید و در نهایت در بین <> نوع داده‌ی خود را مشخص می‌کنید و سپس پرانتز () را قرار می‌دهید. در حال حاضر، شما یک Set خالی خواهید داشت! چرا که هیچ داده‌ای برای آن در نظر نگرفته‌اید. برای این‌کار به شکل زیر عمل خواهیم کرد؛ var set_var = Set<String>() // Or any data type set_var.insert("www.iostream.ir") set_var.insert("www.fanoox.com") set_var.insert("Tegra CMS") set_var.insert("www.ModernCpp.ir") set_var.insert("Apple") set_var.insert("Google") set_var.insert("Apple") set_var.insert("Google") set_var.insert("Microsoft") // Iterator in the set_var for items in set_var { print("Items in the set_var collection : ", items) /* Items in the set_var collection : www.iostream.ir Items in the set_var collection : www.fanoox.com Items in the set_var collection : Tegra CMS Items in the set_var collection : www.ModernCpp.ir Items in the set_var collection : Apple Items in the set_var collection : Google Items in the set_var collection : Microsoft */ } برای این‌که بتوانیم داده‌ای یا مقداری را ذخیره کنیم، از متد insert استفاده می‌کنیم که در بالا هم مشاهده می‌کنید. اما نکته‌ای که به احتمال زیاد شما هم متوجه آن شده‌اید، این است که ما دو بار مقدار Google و Apple‌ را ذخیره کرده‌ایم! اما همان‌طور که در تعریف Set گفتیم، در خروجی فقط یک‌بار این مقادیر نمایش داده می‌شوند، حتی اگر به تعداد زیاد بخواهید داده‌ی تکراری وارد کنید! چرا که این نوع (‌ Set ) فقط مقادیر غیر‌تکراری را ذخیره خواهد کرد. تعریف دیگر آن به این صورت است؛ var setـvar : Set = [1,2,3,4,5,6,7,8,9,10] print(set_var) // Output is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] در این مورد، شما مانند تعیین نوع برای متغییر، نام کلاس Set‌ را بعد از دو نقطه ( که به آن هم کالُن ( : )‌ گفته می‌شود ) آورده و سپس مانند یک آرایه، مقادیر و داده‌های خودتان را در بین دو قُلاب [] می‌نویسید. برای نمایش دادن آن هم کافیست که نام آن را بیاورید، همانند تعریف بالا. این نکته را به یاد داشته باشید که کامپایلر، به صورت ضمنی ( Implicit ) نوع داده را تشخیص داده و نیازی به ذکر نوع داده نخواهید داشت. ویژگی‌ها ( Properties ) و متد‌ها به دست آوردن تعداد اعضا با استفاده از ویژگی count می‌توانید به تعدا اعضای یک Set دسترسی داشته باشید؛ var set_var : Set = [1,2,3,4] print(set_var.count) // Output is 4 بررسی خالی بودن یا نبودن Set و همچنین اگر می‌خواهید خالی بودن یا نبودن را بررسی کنید، isEmpty این امکان را در اختیار شما قرار داده است؛ var set_var : Set = [1,2,3,4,5,6,7,8,9,10] if set_var.isEmpty { print("set_var is empty!") } else { print("set_var not empty") // This will run code } اضافه کردن عضو جدید در حالت عادی نمی‌توانیم به Setهای خودمان عضوی یا داده‌ای اضافه کنیم! در تعریف دوم شما دیدید که ما به صورت مستقیم این کار را انجام دادیم، اما در ادامه‌ی برنامه شاید نظرمان عضو شود و بخواهیم عضو را حذف و عضو دیگری را اضافه کنیم؛ برای این منظور از متد insert استفاده خواهیم کرد؛ var celebrities : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] celebrities.insert("Jeffrey Preston Bezos") celebrities.insert("Sergey Brin") celebrities.insert("Lawrence Lary Page") همان‌طور که مشاهده کردید، ما با استفاده از متد insert می‌توانیم عضو‌های جدیدی را اضافه کنیم. حذف یک عضو برای این کار ما از متد remove استفاده می‌کنیم؛ var celebrities : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] celebrities.remove("Steven Paul jobs") print(celebrities) // Output is ["William Henry Bill Gates lll", "Elon Reeve Musk"] این متد یک آرگومان دریافت خواهد کرد و همان عضوی است که می‌خواهید حذف شود. بررسی وجود داشتن یا نداشتن یک مقدار خاص از متد contains برای این منظور استفاده می‌کنیم؛ var celebrities : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] if celebrities.contains("Elon Reeve Musk") { print("Elon Reeve Musk is available in the celebrities") // This will run code } else { print("Elon Reeve Musk is'n available in the celebrities") } نکته متد‌های کار با Set، با خود Set اصلی کاری نداشته و بسته به متد، یک Set‌ جدید برگردانده می‌شود. گَردش در Set برای این‌که بتوانیم به تک‌تک اعضای یک Set دسترسی داشته باشیم، از حلقه‌ی for استفاده می‌کنیم، به این کار گردش می‌گویند. در جلسه‌ی قبل، توضیحی مختصر در این باره‌ داده‌ایم که می‌توانید مطالعه کنید. پس به گردش در Set به این صورت است؛ var celebrities : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] for items in celebrities { print("Celebrities : ", items) /* Celebrities : Steven Paul jobs Celebrities : William Henry Bill Gates lll Celebrities : Elon Reeve Musk */ } به همین سادگی، می‌توانید بین عضو‌های مختلف گردش کرده و پردازش‌های لازم را بنابر نیاز انجام دهید. مرتب‌سازی در حالت پیش‌فرض، نوع Set هیچ مبنایی برای مرتب‌سازی ندارد و داده‌ها را به صورت تصادفی نمایش خواهد داد؛ در صورتی که بخواهید بر اساس ترتیب داده‌ها را نمایش دهید، می‌توانید از متد sorted استفاده کنید؛ var celebrities : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] for items in celebrities.sorted { print("Sorted celebrities : ", items) /* Sorted celebrities : Elon Reeve Musk Sorted celebrities : Steven Paul jobs Sorted celebrities : William Henry Bill Gates lll */ } فقط توجه داشته باشید که این متد باید در حلقه مورد استفاده قرار گیرد. ایجاد یک Set جدید با ترکیب دو Set یا ( Union ) با استفده از متد union شما می‌توانید یک Set کاملا جدید، ایجاد کنید؛ var celebrities_some_one : Set = ["Steven Paul jobs", "William Henry Bill Gates lll", "Elon Reeve Musk"] var celebrities_two_some : Set = ["Jeffrey Preston Bezos","Sergey Brin","Lawrence Lary Page"] var celebrities_final : Set = celebrities_some_one.union(celebrities_two_some) print(celebrities_final) ذخیره یک مقدار مشترک ( Intersection ) اگر دو Set داشته باشیم و یک مقدار در هر دو یکسان باشد، متد intersection می‌تواند همه‌ی مقادیر دیگر را پاک کرده و فقط همان مقدار را در قالب یک ‌Set‌ جدیدی برگرداند؛ var numbers_one : Set = [1,6,3] var number_two : Set = [5,6,3] var number_final : Set = number_one.intersection(number_two) print(number_final) // Output is [6, 3] همان‌طور که مشاهده کردید، در خروجی مقدار 3 , 6 را خواهیم داشت، چرا که باقی مقدار تکراری نبوده و در نتیجه حذف خواهند شد. حذف مقادیر یک Set و مقادیر مشابه ( Subtracting ) اگر بخواهیم مقادیر یک Set را به طور کامل حذف کنیم و همچنین اگر مقداری تکراری وجود داشت، آن را هم حذف کرده و فقط Set اول را داشته باشیم، از متد subtracting استفاده خواهیم کرد؛ var numbers_one : Set = [1,6,3] var number_two : Set = [5,6,3] var number_final : Set = number_one.subtracting(number_two) print(number_final) // Output is [1] مشاهده می‌کنید که خروجی برابر ۱ است! چرا که Set اول فقط مقدار ۱ را دارد و مابقی مقادیر در Set اول و دوم تکرار هستن و در نهایت حذف خواهند شد. ایجاد یک Set جدید با مقادیر غیر تکراری ( SymmetricDiffernce ) در متد union شما مقادیر غیر‌تکراری را نخواهید داشت و در Set جدیدی که ساخته خواهد شد و یا به طور مستقیم در خروجی نمایش داده می‌شود، فقط یک‌بار مقادیر نمایش داده می‌شوند؛ اما با متد symmetricDifference شما مقادیر دو طرف Set را حذف خواهید کرد و فقط مقادیری که دیگر تکراری نیستند، نمایش داده می‌شود. اجازه بدهید با ذکر یک مثال، این موضوع را شفاف‌تر کنیم؛ var numbers_one : Set = [1,6,3] var number_two : Set = [5,6,3] var number_final : Set = number_one.symmetricDiffernce(number_two) print(number_final) // Output is [1,5] همان‌طور که مشاهده می‌کنید، ما مقادیر ۳ و ۶ را نخواهیم داشت! چرا که در این متد، مقادیر تکراری هر دو Set حذف خواهند شد و داده‌هایی که تکراری نیستند، در یک Set جدید ایجاد می‌شوند. امیدواریم که این جلسه مورد رضایت شما عزیزان قرار گرفته باشد.
  20. Amir Rahmani

    سلام. با توجه به اینکه هر چیزی که در جنگو میسازید یک app محسوب میشه و جنگو قابلیت بسیار خوبی برای به انتشار دادن app های خودتون به صورت پکیج(package) میدهد. در djangopackages میتوانید لیستی از آنهارا مشاهده و یا استفاده بکنید. توسعه ی پکیج جنگو در مرحله ی اول پیشنهاد مستندات رسمی جنگو میگوید اول اسم برنامه ی خود عبارت django- را بگذارید. به عنوان مثال اگر برنامه ی شما اسمی مثل todo باشد اسم به صورت django-todo در می آید. داکیومنت در مرحله ی دوم شما باید برای پکیج خودتون یک راهنما ایجاد بکنید. معمولا در پوشه ی پکیج فایلی به نام README.rst ایجاد میکنند و راهنمارا داخل آن مینویسند. برای مثال برای برنامه ی ما به این گونه میشود : ===== ToDo ===== ToDo is a Django app to conduct Web-based Todo. For each task, jobs can choose a time limit or done it when you want. Detailed documentation is in the "docs" directory. Quick start ----------- 1. Add "todo" to your INSTALLED_APPS setting like this:: INSTALLED_APPS = [ ... 'todo', ] 2. Include the todo URLconf in your project urls.py like this:: path('todo/', include('todo.urls')), 3. Run `python manage.py migrate` to create the polls models. 4. Visit http://127.0.0.1:8000/todo/ to add and finish your todos. البته همانطور که در متن مشاهده میکنید مستندات بیشتر راجب پکیج شما در پوشه ی docs قرار داده میشوند. لایسنس در این بخش به بحث خوب لایسنس میرسیم شما در این مسیر باید محتویات لایسنس خود رو قرار بدید: django-todo/LICENSE بسیاری از پکیج های جنگو تحت لایسنس BSD هستند ولی شما آزادید تا هر چیزی را انتخاب بکنید. setup در این مرحله شما دو فایل setup.cfg و setup.py میسازید تا برنامه شما بیلد و برای نصب آماده شود. آموزش این دو از بحث این مقاله فراتر میرود ولی میتونید به این لینک برای یاد گیری و مطالعه بیشتر مراجعه کنید. در مثال ما فایل setup.cfg بدین گونه میشود. [metadata] name = django-todo version = 0.1 description = A Django app to conduct Web-based todo. long_description = file: README.rst url = https://www.example.com/ author = Your Name author_email = yourname@example.com license = BSD-3-Clause # Example license classifiers = Environment :: Web Environment Framework :: Django Framework :: Django :: X.Y # Replace "X.Y" as appropriate Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Topic :: Internet :: WWW/HTTP Topic :: Internet :: WWW/HTTP :: Dynamic Content [options] include_package_data = true packages = find: و قاعدتا فایل setup.py ما بدین شکل میشود: from setuptools import setup setup() و مراحلی بعدی وجود دارد که اختیاری است مثل اضافه کردن فایل MANIFEST.in برای استفاده کردن از فایل های اضافه و ... . خوب حالا برای اجرای setup.py . بیلد گرفتن از پروژمون از دستور زیر استفاده میکنیم: python manage.py sdist و فایل با پسوند tar.xz ایجاد میشود. تبریک میگم شما پکیج جنگوی خود رو ساختید.
  21. سید عمید قائم مقامی

    Windows Access Control List (ACL) قسمت سوم (مفاهیم) مباحث مورد بررسی در این مقاله: Win32 Object Account Security Identifier (SID) Access Control Entries (AEC) Access Control List (ACL) Discretionary Access Control List (DACL) System Access Control List (SACL) Security Desctiptors (SD) Win32 Objects: سیستم عامل ویندوز قادر به ایجاد چندین نوع مختلف از اشیاء Win32 است. این اشیائ به گونه ای هستند که دارای حافظه می باشند. نمونه هایی از این اشیاء عبارتند از: Files Directories Registry keys Network shares Printer shares Semaphores Mutexes Process tokens و .... ماهیت این اشیاء به نوعی است که سیستم عامل می تواند آنها را در هر حافظه ای ذخیره کند (مانند رم، فلاپی، هارد دیسک، ذخیره در درایو شبکه، انتقال آن به دیگر کامپیوتر های روی شبکه). به عنوان مثال، فایل یک شئ از سیستم عامل می باشد. زمانی که شما یک فایل می سازید(مثلا یک فایل Microsoft word) سیستم عامل با آن به عنوان یک شئ برخورد می کند. حتی ممکن است بخواهید که فایل را داخل یک درایو ذخیره کنید تا بعدا به آن دسترسی داشته باشید. برای سیستم عامل مهم نیست که شما می خواهید شئ را در یک درایو ذخیره کنید یا نه، تنها این موضوع را می داند (اهمیت می دهد) که آن شئ مقداری از فضا را اشغال می کند (چه در رم یا جای دیگر). شئ پایدار (persistent object) شئی است که در دستگاه های حافظه بلند مدت ذخیره می شوند (مانند هارد دیسک) به گونه ای که داده ها بعدا قابل بازیابی باشند(ارساله داده به کارت صدا یا چاپگر باعث ایجاد شئ پایدار نمی شود، زیرا سیستم عامل بعدا قادر به بازیابی داده های آن نمی باشد). Win32 سیستم عامل به طور معمول در روز ده ها هزار شئ را مدیریت می کند. Win32 سیستم عامل تقریبا فقط یک مدیر اجرائی پیچیده به حساب می آید (object manager). از آنجایی که سیستم عامل با اشیاء در ارتباط است میداند که مواقعی وجود دارد که اشیائ باید ایمن شوند. مثلا زمان ذخیره یک پرونده حساس در یک هارد درایو، باید از کاربران غیر مجاز حفظ شود. Securing Objects: Win32، مکانیزمی را برای تامین امنیت انواع متفاوتی از اشیاء فراهم کرده است. به بیان ساده تر، تامین امنیت یک شئ به این معنی است که مجموعه ای از مجوز ها ایجاد شود که با شئ مورد نظر مرتبط باشد. هر شئ می تواند برای خو یک سری مجوز ها داشته باشد. Accounts: در قلب سیستم امنیتی Win32 همان چیزی است که به عنوان حساب کاربری (account) شناخته می شود. این همان چیزی است که فرد هنگام ورود به سیستم می تواند مشخص کند، و این به عنوان user account شناخته می شود. User account از یک نام شناسایی (Userid)، یک رمز عبور و یک دسته اطلاعات دیگر که فقط مربوط به آن کاربر خاص است، شامل می شود. انواع دیگری از حساب های کاربری وجود دارد. این حساب ها مانند حساب کاربری هستند به جز اینکه آنها کاربری را معرفی نمی کنند، بلکه انواع دیگری از حساب را معرفی می کنند. انواع حساب های کاربری عبارتند از: User accounts Machine accounts Domain accounts Group accounts حساب های user ،machine و domain همه دارای رمزعبور هستند. رمز ورود یک حساب کاربری به طور خودکار توسط Primary Domain Controller (PDC) و دستگاه مدیریت می شود. آخرین حساب، حساب group account می باشد. برخلاف حساب های دیگر، این حساب رمز عبور ندارد. دو نوع مختلف از حساب های گروهی وجود دارد: Local group و global group. تفاوت این دو فراتر از محدوده این مقاله می باشد، اما باید این را بدانید که حساب کاربریی وجود دارد که فاقد رمز عبور است. به طور معمول حساب های کاربری دارای نام هایی مانند زیر هستند: Administrator Guest Joel Group accounts نام هایی مانند نام های زیر دارند: Guests Domain Admins Managers چندین حساب سیستم وجود دارد که نمی توان آنها را مدیریت کرد و دارای معانی ویژه ای هستند: Everyone (all users) CREATOR OWNER (the owner and creator of a given object) SYSTEM (the Win32 system kernel) نکته دیگر اینکه اگر به یک حساب کاربری در یک دامنه دیگر اشاره می کنید، می توانید نام دامنه و backslash را به نام حساب اضافه کنید: Roth Consulting\Joel Roth Consulting\Domain Admins Microsoft\Gates Accounting\Managers دو دامنه از پیش تعریف شده وجود دارد که قابل اصلاح یا تغییر نیستند و دارای معانی خاصی هستند: BUILTIN (نام حساب کاربری) BUILTIN\Administators BUILTIN\Guests NT AUTHORITY (نماینده سیستم امنیتی داخلی است) NT AUTHORITY\SYSTEM Security Identifiers (SID): هر زمان که یک حساب کاربری جدید (یا گروه) ایجاد شود باید منحصر به فرد و از دیگر حساب های کاربری جدا باشد. اگر یک حساب کاربری با نام کاربر Joul ساخته شود، درحالی که قبلا یک حساب با همان userid موجود بوده باشد، ساخت حساب کاربری جدید با شکست مواجه می شود. اگر حساب کاربری به درستی ایجاد گردد، Win32 یک Security Identifier (SID) می سازد. SID یک ساختار داده است که یک حساب خاص را در یک دامنه خاص مشخص می کند. هر SID منحصر به فرد است و Win32 هیچوقت دو SID مشابه تولید نمی کند. حتی اگر شما دو حساب با نام Joel در دو دامنه (domain) داشته باشید، قطعا domain1\Joel و domain2\Joel دارای SID های متفاوت می باشند. هنگامیی که Win32 سیستم عامل می خواهد به یک حساب کاربری، حساب ماشین، حساب دامنه یا گروه (محلی و عمومی) مراجعه می کند به SID آن اشاره می کند. زمانی که ما به یک حساب یا گروه با اسم (مانند domain1\Joel) مراجعه می کنیم Win32 به دنبال SID مخصوص دامنه و حساب می گردد. برای آسان کردن کار برای ما (نه رایانه) Win32 یک SID را با یک رشته طولانی مانند زیر نمایش می دهد: S-1-5-21-143984352-578909669-1869494990-1001 این رشته منحصر به فرد یک کاربر منحصر به فرد را در یک دامنه مشخص می کند. typedef struct _SID { BYTE Revision; BYTE SubAuthorityCount; SID_IDENTIFIER_AUTHORITY IdentifierAuthority; #if ... DWORD *SubAuthority[]; #else DWORD SubAuthority[ANYSIZE_ARRAY]; #endif } SID, *PISID; Revision: نسخه SID ساختار موجود در یک SID مشخص را نشان می دهد. SubAuthorityCount: نشان دهنده تعداد مقادیر subauthority در یک SID است. IdentifierAuthority: بالاترین سطح امتیاز را که SID می تواند برای یک نوع مشخص از مدیر امنیتی مشخص کند شامل می شود. SubAuthority: اطلاعات مهمی را در SID نگه می دارد، که شامل یک یا تعداد بیشتری از مقادیر، از جنس subauthority می باشد. آخرین مقدار یک دامنه در یک (شبکه) enterprise را مشخص می کند. این داده domain identifier نامیده می شود(به غیر از داده آخر موجود در آرایه). آخرین مقدار در این سری (آرایه subauthority)، relative identifier (RID) نامیده می شود. بعد از تبدیل یک SID از فرمت باینری به یک فرمت رشته، فهم آسان تری پیدا می کند: S-R-X-Y1-Y2-Yn-1-Yn S: نشان دهنده این است که این رشته یک رشته SID است. R: نشان دهنده سطح revision است. X: نشان دهنده مقدار identifier authority است. Y: نشان دهنده یک سری از مقدار های subauthority می باشد (n مشخص کننده شماره هر مقدار است). به طور خلاصه می توان گفت که SID همان چیزی است که WIN32 استفاده می کند تا یک حساب کاربری را در یک دامنه خاص مشخص کند. Access Control Entries (ACE): ما حالا میفهمیم که Win32 برای اشاره به یک حساب کاربری، از SID به عنوان یک راه آسان استفاده می کند. اما چگونه یک حساب کاربری (یک SID) با مجموعه ای از مجوز ها مرتبط می شود؟ Win32 به permission ها به عنوان یک کنترل دسترسی (Access Control) مراجعه می کند. اگر شما یک شئ داشته باشید (به عنوان مثال، یک فایل در یک هارد درایو)، باید در مدیریت دامنه یتان(Managers domain) آن مسئله که می تواند مشکل ساز باشد (به عنوان مثال joel) را کنترل کنید، به عنوان مثال می توان فایل را برای این عنصر مشکل ساز فقط قابل خواندن قرار دهید. شما می توانید این کار با ایجاد یک Access Control Entry انجام دهید(ACE). یک ACE یک راه آسان برای نگاشت یک اکانت (SID) به مجموعه ای از permission ها است. مجموعه ای از pemission های ACE با نام permission mask(یا به طور خلاصه mask) شناخته می شوند. Win32 در داخل خود این permission mask ها رو به صورت یک مقدار DWORD چهار بایتی ذخیره می کند. هر یک از permission ها با یک بیت مشخص هماهنگ می شود. این به این معنا است که زمانی که یک ACE ساخته می شود، permission های آن به گونه ای ساخته می شوند که می توانند با OR های منطقی permission های متفاوت ایجاد کنند. $Permission = READ | WRITE | DELETE; بنابر این شما اگر می خواهید به حساب کاربری(کاربران) به عنوان مثال Joel اجازه خواندن، نوشتن و پاک کردن یک فایل را بدهید، باید یک ACE بسازید که شامل SID مدیر(یا همانjoel ) و یک permission mask (READ | WRITE | DELETE) باشد. شما می توانید ساختار کلی یک ACE را در زیر مشاهده کنید: Access Control List (ACL): هر مدیری که سرورهای پرونده را مدیریت می کند می تواند به شما بگوید که چه مجوزهایی روی یک پرونده قرار داده شده است. به عنوان مثال یک فایل می تواند شامل permission های زیر باشد: هر یک از مجموعه account/permission های کاربر ها تمام اطلاعات لازم را برای ساخت یک ACE را دارند. بر فرض که شما می خواهید چندین ACE بسازید، یک نمونه از مجموعه اطلاعات لازم account/permission در عکس بالا توصیف شده است. برای اینکه ACE ها مفید باشند، آنها باید با یکدیگر گروه بندی شوند که بتوان آنها را برای برخی از اشیاء مانند فایل ها اعمال نمود. گروهی از ACE ها با نام Access Control List (ACL) شناخته می شوند. سیستم عامل های Win32 از ACL برا گروه بندی منطقی ACE ها استفاده می کنند به طوری که آنها به راحتی می توانند بر روی یک شئ اعمال شوند. درست همانطور که یک دایرکتوری می تواند شامل گروهی از فایل ها باشد، یک ACL نیز می تواند شامل گروهی از ACE ها باشد. هر شئ Win32 که می تواند قابلیت secured را داشته باشد، از ACL برای تعیین کردن این که چه کسی به آن شئ دسترسی داشته باشد یا نداشته باشد استفاده می کند. زمانی که شما مالک شئی می شوید، یک دسته از ACE هایی را که به حساب کاربر (SID) (که در مجموعه permission ها قرار داده اید (permission mask) ) نگاشت شده است را ساخته اید. دو نوع ACL وجود دارد. نوع اول Discretionary Access Control List (DACL) می باشد، و نوع دوم System Access Control List (SACL) می باشد. تفاوت بین دو نوع مختلف ACL: DACL: این نوع ACL یک لیست از ACE ها است که مشخص می کند چه کسی حق دارد یا ندارد که به شئ دسترسی داشته باشد. برای مثال ممکن است یه یک اکانت اجازه خواندن، نوشتن یا پاک کردن را داشته باشد یا اجازه آن رد شده باشد. این همان چیزی است که هنگام تغییر مجوز ها در Windows Explorer یا File Manager تنظیم می شود. SACL: این نوع از ACL شامل لیستی از ACE ها است که نشان می دهد Win32 آیا باید رویدادهای خاصی را در Log Event وارد کند یا خیر. یک رویداد ممکن است شامل موارد زیر باشد : خواندن شئ نوشتن شئ اجرای شئ پاک کردن یک شئ تغییر مجوز های (permission) شئ گرفتن مالکیت شئ (تغییر مالک) اکثر کاربران بیشتر با DACL ها سر و کار دارند و کمتر با SACL درگیر می شوند، مگر اینکه شما سرپرستی باشید که دقیقاً باید تلاش کاربران برای دستیابی به اشیاء خاص را نظارت کند. بنابراین یک DACL نشان دهنده این است که کدام کاربران دسترسی خاصی دارند و SACL نشان دهنده این است که کدام رویداد وارد شده (logged) برای کدام کاربر است. Object Owners and Groups: گذشته از ACE های و ACL ها بیشتر اشیاء می توانند یک صاحب (owner) و گروه (group) داشته باشند. Object’s owner به طور ساده یک حساب کاربر است که object را ساخته است. انتصاب مجدد مالک به یک شئ امکان پذیر است اما فرض موجود این است که می توان کاربری را که شی را ساخته ردیابی کرد. Object’s group به طور ساده یک global group است که نشان دهنده default group سازنده می باشد. Win32 از این استفاده نمی کند اما برای سازگاری با POSIX وجود دارد. هر شئ که با یک مالک و گروه در ارتباط باشد از SID ها برای نشان دادن حساب و گروه استفاده می کنند. به بیان دیگر: یک owner و group، SID های هستند که نشان دهنده یک user account و group کسی هستند که مسئول شئ می باشند. Security Descriptors (SD): در این مرحله باید بدیهی باشد که امنیت یک شئ مستلزم ایجاد یک دسته از ACE ها (نگاشت userid ها به permission mask ها) و گروه بندی آن ها در یک لیست است(چه DACL، ACL یا هر دو). SID ها user account را مشخص می کنند، و لازم است با global group جمع گردد. پس از بدست آوردن همه این چیز ها آنها را می توان به صورت مرتب در یک ساختار بزرگ به نام توصیف کننده امنیتی (Security Descriptor) قرار داد. پس از ایجاد یک SD، می توان آن را در یک securable object مورد استفاده قرار داد. یک SD را میتوان از فایل A استخراج نموده و به عنوان مثال برای فایل B مورد استفاده قرار داد. در این حالت تمام مقادیر DACL و SACL فایل A برای فایل B ذخیره می شود. SD موجود را می توان با استخراج آن از securable object موجود دریافت کرد یا آن را از ابتدا ایجاد کرد. بنابر این شئ می تواند از یک شئ موجود(مانند یک فایل) بدست آمده یا اصلاح شده و سپس در یک securable object دیگر استفاده گردد. اصلاح، می تواند شامل هر چیزی مانند اضافه کردن ACE های جدید، تغییر permission mask در داخل یک ACE یا حذف ACE ها گردد. پایان قسمت سوم
  22. سید محمد عباسی

    اگه برای کار‌های ریز‌کاری باشه که بخواهیم دقیق باشیم، فک کنم Auto کاربرد داشته باشه که خروجی دیزاسمبل رو بخوایم ببینیم.
  23. سید محمد عباسی

    ممنون از این مطلب ساده و روان. مورد آخر که Auto باشه رو به خوبی متوجه نشدم! البته متوجه‌ی این نشدم که چرا باید نوع خروجی رو داشته باشیم؟
  24. سید محمد عباسی

    آموزش زبان برنامه‌نویسی سوئیفت - جلسه ششم مواردی که در این جلسه یاد خواهید گرفت: آرایه‌ها (‌ Arrays ) آرایه‌‌ها چیستند؟ اگر شما یک بسته کِبریت را تصور کنید، حداقل تا 50 تا دانه کبریت داخل آن است! همه‌ی آن‌ها هم همه کبریت هستند نه چیز دیگر. البته می‌شود چیز دیگری هم گذاشت داخل آن اما در برنامه‌‌نویسی نمی‌توانید همچین کَلکی را سوار کنید! ( در بعضی از زبان‌های برنامه‌نویسی ). آرایه‌ها در سوئیفت مجموعه‌ای از مقادیر را داخل خودشان نگه‌داری می‌کنند و مانند یک متغییر می‌مانند با این تفاوت که یک متغییر معمولی یک مقدار رو می‌تواند ذخیره کند ولی متغییر آرایه‌ای این‌گونه نیست. به این دنباله‌ی عددی دقت کنید؛ 1,2,3,4,5,6,7,8,9,10 به‌نظر شما می‌شود این‌ها را در یک متغییر معمولی ذخیره کرد؟‌ به طوری که هر کدام از عدد‌ها را که بخواهیم سریع به ما بدهد؟ مسلما خیر. تازه اگر شما بخواهید برای هر کدوم از این اعداد، یه متغییر جداگانه تعریف کنید؛استاندارد پروژه‌‌تان به شدن پایین می‌آید و زبانی هم که با آن برنامه‌نویسی می‌کنید به همان مقدار به شما واکنش نشان خواهد داد ( هر کُنشی واکنشی دارد...! ) کاهش سرعت اجرا، زیاد‌نویسی، اگه برای سایر توسعه‌دهنده‌های دیگر باشد، به احتمال زیاد طرف پروژه‌ی شما نمی‌آیند! به عنوان مثال، تعریف متغییر بدون آرایه؛ let number_one : Int8 = 1 let number_two : Int8 = 2 let number_three : Int8 = 3 let number_four : Int8 = 4 let number_five : Int8 = 5 let number_six : Int8 = 6 let number_seven : Int8 = 7 let number_eight : Int8 = 8 let number_nine : Int8 = 9 let number_ten : Int8 = 10 خودتان باشید، حاضرید به این شکل پروژه‌‌ای را به این شکل پیش ببرید؟! حالا فکر کنید ۱۰۰۰ داده‌ی مختلف بخواهید تعریف کنید! تعجب می‌کنید، اینطور نیست؟! اما خوشبختانه همیشه راه نجات هست... آن هم استفاده از آرایه‌ها است! آرایه‌ها به شما کمک می‌کنند تا داده‌های زیادی که مورد نیاز پروژه‌ی شما باشد، تعریف و استفاده کنید. نحوه‌ی تعریف به سه روش می‌توانید آرایه‌های خودتان را تعریف کنید؛ استفاده از نوع و سازنده ( Construct ) استفاده از کلاس Array، نوع متغییر و سازنده تعریف آرایه بدون هیچ واسطه‌ای! نکته آرایه‌های سوئیفت از موقعیت ( Index ) صفر (‌ ۰ ) شروع خواهند شد. در ادامه متوجه این موضوع خواهید شد، اما همیشه این را به خاطر داشته باشید که اولین عضو در یک آرایه در موقعیت صفرم آن آرایه قرار می‌گیرد و به همین ترتیب، موقعیت اول، حاکی از مقدار دوم، موقعیت دوم، مقدار سوم و الی آخر... پس این مورد را در پروژه‌هایتان حواستان باشد! چرا که با یک اشتباه، ساعت‌ها زمان و انرژی خود را تَلف می‌کنید تا یک اشتباه کوچک را پیدا و رفع کنید. اولین مورد که در لیست بالا گفته شده،‌ به این صورت است که شما یک متغییر تعریف می‌کنید ( از کلمات کلیدی var و یا let می‌توانید استفاده کنید) و بعد یک نام شفاف و دلخواه خواهید نوشت و در نهایت از نوع و سازنده به این شکل استفاده می‌کنید؛ var first_array = [Int]() print(first_array) // Output is empty or [] در روش دوم می‌توانید از کلاس Array‌ استفاده کنید! به این شکل که مشاهده می‌کنید؛ var first_array = Array<Int>() print(first_array) // Output is empty or [] و در نهایت می‌توانیم بدون هیچ واسطه‌ای ( بدون استفاده روش اول و دوم )، آرایه‌مان را تعریف کنیم؛ var first_array = [1,2,3,4,5,6,7,8,9,10] print(first_array) // Output is [1,2,3,4,5,6,7,8,9,10] به نظر می‌آید که این برای شما راحت‌تر و قابل‌درک‌تر باشد تا موارد قبل،‌ اینطور نیست؟ اما خُب، آن‌ها را هم برای دوستانی که عاشق پیچدگی هستن نوشتیم! شما می‌توانید از هر روشی که مد‌نظرتان بود استفاده کنید‌. نوع داده‌ای هم که مشخص می‌کنید، بستگی به پروژه‌ی شما دارد؛ var first_array = ["www.iostream.ir","www.fanoox.ir","www.ModernCpp.ir"] print(first_array) // Output is empty or ["www.iostream.ir", "www.fanoox.ir", "www.ModernCpp.ir"] همین‌طور شما می‌توانید از مقادیری پیش‌فرض برای آرایه‌هایتان ستفاده کنید! به عنوان مثال شما در همان موقعی که تعریف آرایه‌ی خودتان را انجام می‌دهید، می‌خواهید تا ۱۰ خانه از آرایه‌تان داده‌های تکراری داشته باشد. این‌کار را می‌توانید با استفاده از سازنده‌ (‌ Construct ) انجام بدهید؛ var first_array = [Int](repeating: 20, count: 10) print(first_array) // Output is [20,20,20,20,20,20,20,20,20,20] در کد بالا گفته شدهکه عدد ۲۰ را ( repeating: 20 ) به تعداد ۱۰ بار ( count: 10 ) در آرایه‌ی first_array قرار گیرد. خروجی را همان‌طور که مشاهده می‌کنید، مقدار ۲۰ به تعداد ۱۰ بار در first_array تکرار شده. ترکیب دو آرایه با هم برای این‌کار فقط کافی است با استفاده از آپِریتر +، یک آرایه‌ی جدید بدست بیاوریم؛ var first_array = [1,2,3,4,5] var two_array = [6,7,8,9,10] var result_array = first_array + two_array print(first_array) // Output is [1,2,3,4,5,6,7,8,9,10] دسترسی به کُل یا یک از داده‌های آرایه برای این‌که بتوانید به کل آرایه دسترسی داشته باشید و آن رو نمایش بدهید، می‌توانید به این صورت عمل کنید؛ var first_array = [1,2,3,4,5] print(first_array) // Ouput is [1,2,3,4,5] البته می‌توانید در مواقعی که نیاز به آرایه کامل دارید استفاده کنید! به عنوان مثال پارامتر توابع و ... برای این‌که بتوانید به یک داده‌ی خاص که مورد‌نظر شما است، دسترسی داشته باشید؛ باید از موقعیت ( Index ) آرایه استفاده کنید؛ var website_name = ["www.iostream.ir","www.fanoox.com","www.ModernCpp"] var langauge_name = ["C++","C","Swift"] print(website_name[0]) // Output is www.iostream.ir print(langauge_name[2]) // Ouput is Swift در قطعه کد بالا، ابتدا باید نام آرایه بیاورید و بعد از آن با استفاده از دو براکت ( [] ) و قرار دادن شماره موقعیت، به مقدار آن دسترسی داشته باشید. همان‌طور هم که مشاهده می‌کنید، گفتیم که آرایه‌ها از موقعیت صفرم شروع می‌شوند که در بالا با آوردن website_name و سپس شماره موقعیت ( Index Number ) به اولین عضو آن یعنی www.iostream.ir دسترسی پیدا کرده و آن را نمایش می‌دهیم. اگر از ۲ برای آرایه استفاده کنید، به شما خروجی Swift‌ را خواهد داد! چرا که عضو دوم آرایه‌ی langauge_name مقدار Swift است. اضافه کردن داده به آرایه اگه بخواهیم یک عضو (‌ مقدار جدید ) به آرایه اضافه کنیم، از متُد append که مربوط به کلاس آرایه‌ها است، استفاده می‌کنیم. در جلسه‌های بعد با کلاس‌ها، و متد‌ها آشنا خواهید شد. به این مثال توجه کنید؛ var first_array = [1,2,3,4,5] // ‌Before print("Before \(first_array)") // Ouput is [1,2,3,4,5] first_array.append(6) // After print("After \(first_array)") // Output is [1,2,3,4,5,6] با استفاده از موقعیت ( Index ) می‌توانیم یک مقدار را به یک موقعیت یا همان خانه‌ای از آرایه که مشخص کردیم، اضافه کنیم؛ var first_array = [1,2,3,4,5] first_array[0] = [10] print(first_array) // Output is [10,2,3,4,5] یا می‌توانیم از آپِریتر ترکیبی =+ استفاده کنیم برای اضافه کردن گروهی از داده‌ها؛ var first_array = [1,2,3,4,5] // ‌Before print(first_array) // Ouput is [1,2,3,4,5] first_array += [6,7,8,9,10] // After print(first_array) // Output is [1,2,3,4,5,6,7,8,9,10] با استفاده از متد insert می‌توانید یک عضو جدید را به موقعیتی ( Index‌ ) که مد‌نظرتان است، اضافه کنید! به عنوان مثال در آرایه‌ی زیر، می‌خواهید که عدد ۳، در خانه‌ یا موقعیت آخر قرار بگیرد؛ var first_array = [1,2,3,4,5] // ‌Before print("Before \(first_array)") // Ouput is [1,2,3,4,5] first_array.insert(6, at: 4) // After print("After \(first_array)") // Output is [1,2,3,4,5,6] این متد دو پارامتر دریافت می‌کند؛ یکی برای عضو جدید و یکی دیگر برای موقعیتی که می‌خواهید عضو جدید قرار بگیرد که همان‌طور که مشاهده می‌کنید، مقدار ۶ در موقعیت آخر قرار گرفته است که مقدار ۵ را دارد. حذف یک عضو برای حذف یک مقدار از آرایه، از متد‌های remove و removeLast استفاده می‌کنیم. اولین متد‌؛ یک موقعیت مشخص برای حذف عضو می‌خواهد، در حالی که متد دوم؛ آخرین عضو را حذف می‌کند؛ var first_array = [1,2,3,4,5] // ‌Before remove print("Before \(first_array)") // Ouput is [1,2,3,4,5] first_array.remove(at: 0) // After remove print("After \(first_array)") // Output is [2,3,4,5] // Using removeLast first_array.removeLast() print(first_array) // Output is [2,3,4] با استفاده از at: 0 در متد remove مشخص کردیم که می‌خواهیم عضو اول را حذف کنیم که در خروجی هم می‌توانید ببینید و در متد removeLast آخرین عضو آرایه ( در این مثال 5 ) رو حذف کردیم. و در نهایت شما می‌توانید در یک رِنج ( بازه‌ )‌ مشخص؛ که تعیین می‌کنید، عضو اضافه کنید! چیزی شبیه به حلقه‌ی for! این مثال را ببینید؛ var first_array = [1,2,3,4,5] first_array[0...4] = [6,7,8,9,10] print("Before \(first_array)") // Ouput is [6,7,8,9,10] گردش در آرایه گردش یعنی این‌که بتوانیم بین اعضای یک آرایه که یکی یکی آن‌ها را انتخاب می‌کنیم، کارهایی را که مد‌نظرمان است را انجام دهیم! که با استفاده از یک حلقه‌ی for می‌توانیم در عضو‌های آن این کار را انجام دهیم. به این مثال توجه کنید؛ var first_array = [1,2,3,4,5] for item in first_array { print("Item : \(item)") } /* Item : 1 Item : 2 Item : 3 Item : 4 Item : 5 */ اگه بخواهیم که هم موقعیت ( Index ) و هم مقدار آرایه رو داشته باشیم؛ از متد enumerated استفاده می‌کنیم که به ما یک تاپِل ( Tuple ) خواهد داد ( در جلسات بعد بعدا مفصلا درباره‌ی تاپل‌ صحبت خواهیم کرد) و در حلقه استفاده می‌کنیم؛ var first_array = [1,2,3,4,5] for (index,item) in first_array.enumerated() { print("Index of : \(index) and Item : \(item)") } /* Index of : 0 and Item : 1 Index of : 1 and Item : 2 Index of : 2 and Item : 3 Index of : 3 and Item : 4 Index of : 4 and Item : 5 */ Count و isEmpty ( دو ویژگی از کلاس آرایه )‌ شاید بخواهید بدانید تعداد عضو‌های یک آرایه به چه تعدا هستند؟ این مورد به خصوص در حلقه‌ها و یا موقعیت‌های دیگر بسیار موثر خواهد بود! برای این‌کار از ویژگی (‌ Property ) به نام count استفاده می‌کنیم که فقط خواندنی ( read-only ) است و هیچ مقداری رو نمی‌توانید به آن ضافه کنید! (‌ درمورد ویژگی‌ها مفصلا در جلسات بعد توضیح خواهیم داد ) و فقط می‌توانید از مقدار آن که تعداد اعضای آرایه است، استفاده کنید؛ var first_array = [1,2,3,4,5] print(first_array.count) // Output is 5 اگه هم می‌خواهید خالی بودن یا نبودن یک آرایه را بررسی کنید؛ متد isEmpty‌ این امکان را در اختیار شما قرار داده است؛ var first_array = [1,2,3,4,5] if first_array.isEmpty { print("Empty") } else { print("Not Empty") } // Not Empty امیدواریم که این جلسه مورد رضایت شما عزیزان باشد.
  25. الهه انصاری

    ۱۵ اصول راهبردی برای محققان UX

    ما متوجه شده‌ایم که بسیاری از محققان حوزه‌ی تجربه کاربری برای اولین بار هنگام شروع کار با طراحی تجربه کاربری سوالات و نگرانی‌های مشابهی دارند. بنابراین، فکر کردیم که برای جمع آوری تعدادی از اصول مهم مورد نیاز محققان این حوزه، برخی از متداول‌ترین سوالات را مطرح کنیم. البته این مطلب راهنمای کاملی برای تحقیقات تجربه کاربری نیست (تعدادی منابع نسبتاً سنگین و حجیم خارج از این مطلب وجود دارد) اما این مقاله یک نقطه‌ی شروع خوب برای پاسخگویی به برخی از سوالات به ظاهر آزار دهنده‌ی تجربه کاربری است. ترکیب کنید بهترین پژوهشگران از یک ابزار خاص برای انجام تحقیقات خود استفاده نمی‌کنند. آن‌ها روش‌ها و ابزارهای مختلفی را به کار می‌گیرند و سپس آن‌ها را با هم ترکیب می‌کنند. این کار به شما شانس بیشتری برای پیدا کردن مسائل واقعی می‌دهد و سپس می‌توانید برای بهبود آن‌ها اقدام کنید. فهمیدن این که اشتباه کرده‌اید آسان‌تر است وقتی دچار خطا شدیم، تحقیقات می‌توانند به سرعت به ما کمک کنند. اگر یک ویژگی جدیدی اضافه کنید و پنج شرکت کننده‌ی اول تحقیق شما آن را نپسندند، احتمالاً مشکلی در این میان پیش آمده است. با این حال، صد نفر می‌توانند بدون اظهار نظر از چیزی استفاده کنند که ممکن است کارایی نداشته باشد. شما نمی‌توانید اندازه‌ی کلیه‌ی تحقیقات خود را استاندارد کنید متأسفیم، اما اندازه‌ی نمونه‌ها باید براساس میزان خطراتی که در هر تحقیق تخمین زده‌اید معین و براساس نوع تحقیقاتی که انجام می‌دهید محاسبه شود. برای تمام تحقیقات خود سعی نکنید از یک اندازه‌ی واحد استفاده کنید زیرا این عمل یک رویکرد ناقص و نا کارآمد است. انجام آزمایش و تست فقط با یک کاربر همیشه بی معنی نیست تصور کنید که شما یک بسته‌‌ی جدید پردازش کلمه را ایجاد می‌کنید و با اولین کاربر خود سعی می‌کنید یک سند را ذخیره کنید و می‌بینید که روند خراب است. به چند کاربر دیگر برای آزمایش این مورد نیاز دارید؟ هیچ کاربر دیگری، درست است؟ برخی از مشکلات عام هستند و فقط یک کاربر مورد نیاز است تا آن‌ها را کشف کند. اندازه‌های نمونه را برای دقت بیشتر افزایش دهید هرچه اندازه‌ی نمونه‌ی شما بزرگ‌تر باشد، احتمال دقیق‌تر بودن اطلاعات شما بیش‌تر است. یک قانون کلی وجود دارد که می‌گوید دقت را دو برابر کنید تا اندازه‌ی نمونه را با ضریب چهار افزایش دهید! تصادفی سازی می تواند بر نقایص طراحی تحقیق غلبه کند اگر می‌توانید ترتیب سوالات، پاسخ‌ها، جریان فرآیند و غیره را تغییر دهید، آن را انجام دهید. مسیری که شما طی می‌کنید تصادفی‌تر است - به احتمال زیاد می خواهید ثبات پیدا کنید و نقص طراحی آزمایشی را به حداقل برسانید. نتایج تحقیقات متعلق به هیچ کس نیست تمام داده‌هایی که جمع می‌کنید، متعلق به شما یا تیم شما نیست بلکه متعلق به شرکت است. هرچه بیش‌تر تجربه کاربری خود را در مورد شرکت به انجام برسانید احتمال بیش‌تری وجود دارد که شرکت شما شروع به تمرکز بر نیازهای کاربر به عنوان یک اولویت خواهد کرد. یک سیلوی تجربه کاربری را در کسب ‌و کار خود ایجاد نکنید؛ اجازه دهید جریان داده جاری شود. درجه‌بندی مقیاس در سوالات مهم نیست مطمئناً بحث‌های زیادی در مورد این که آیا مقیاس x دقیق‌تر از مقیاس y است یا نه وجود دارد و این که آیا شما باید یک رتبه بی‌طرف داشته باشید یا خیر. هیچ یک از آن‌ها به اندازه‌ی کافی مهم نیستند که بیش از ۵ دقیقه را صرف نگرانی کنید. یک مقیاس را انتخاب کرده و شروع به انجام تحقیق کنید. شرکت کنندگان نیاز به بازتاب پرسوناها دارند همه‌ی افراد، کاربر یا حتی کاربر احتمالی محصول شما نیستند. هر کاربر متناسب با بازار هدف شما نیست. شخصیت‌های کاربر خود را استخراج کنید و به عضویت شخصیت بپردازید؛ به این ترتیب بیش‌ترین شانس دریافت نتیجه را دارید که در واقع برای کاربران هدف شما کار می‌کند. شما نمی‌توانید همیشه به همه‌ی مردم لطف کنید و متخصصان تجربه کاربری حتی نباید سعی در انجام این کار داشته باشند. آنچه می‌گویند در مقابل آنچه انجام می‌دهند غالباً گفته می‌شود که آن چه مردم انجام می‌دهند مهم است و نه آن چه که می‌گویند. ما موافق نیستیم شما باید هم آنچه را که افراد می‌گویند و هم آنچه انجام می‌دهند را بسنجید. سپس می‌توانید دلایل قطع ارتباط بین دو موقعیت را کشف کنید. بعضی اوقات مردم واقعاً آن چه را که می‌گویند می‌خواهند و بعضی وقت‌ها این کار را نمی کنند. دانستن زمان واقعی بودن این موارد برای تجربه کاربر است. جعبه‌ی ابزار خود را توسعه دهید قرار است به کمک توانایی‌ها و موارد تخصص خود، ایده‌ها و روش‌های جدیدی برای طولانی مدت تولید کنید. بدون امتحان کردن ابزاری آن را رد نکنید. حتی اگر آن‌ها به کار شما نیایند، به جای فرض کردن این ناکارآمدی را کاملا تجربه خواهید کرد. در بسیاری موارد حتی بدترین ابزارها اگر به درستی تطبیق داده شوند، می‌توانند بینش معقولی ارائه دهند. قابلیت استفاده - یک تخیل معقول؟ اندازه گیری قابلیت استفاده غیرممکن است. معیاری که می‌توانیم اندازه گیری کنیم مربوط به غیرقابل استفاده بودن آن است. این اقدامات انعطاف پذیر هستند؛ آن‌ها از یک محصول به محصول دیگر، از یک کاربر به کاربر دیگر و از یک محقق تجربه کاربری به محقق دیگر تغییر می‌کنند. بسیار خب، یافتن مشکلات بخشی از تحقیقات است. ما از قبل می‌دانیم که نشان دادن این که مشکلی نیست، خیلی سخت‌تر از یافتن مشکل است. گزارش ها را کوتاه تهیه کنید مطمئناً این روش شگفت انگیز و مبتکرانه بوده و نتایج باورنکردنی دارد اما شما نیازی به نوشتن کتاب برای دستیابی به این مسئله ندارید. اگر می‌خواهید تحقیقات شما دارای ارزش گسترده‌ای در سازمان باشد، طول گزارش‌های خود را به حداقل برسانید. با این حال، اجازه ندهید که این موضوع شما را از ایجاد کار دقیق‌تر به عنوان ابزاری یادگیری در محیط شخصی خود یا نوشتن کتابی در این حوزه در صورتی که قصد انتشار آن را دارید باز دارد. آگاه باشید که ناظران به طور متفاوتی بررسی می‌کنند دلیلی وجود دارد که پلیس با شهادت شاهد عینی با یک بدبینی سازنده برخورد می‌کند. افراد آن چه را که قصد دیدنش را دارند، می‌بینند و به ندرت شاهدان آن چیزها را خواهند دید. این مشکل بزرگی نیست؛ در حقیقت، اضافه کردن ناظران ممکن است موفقیت کلی تحقیقات را افزایش دهد. اگر شما همه‌ی مشکلات مختلف را شناسایی کنید، برای کاربران بهتر است (تا زمانی که شما قصد اصلاح همه‌ی آن مشکلات را دارید) و فراموش نکنید که عمل مشاهده نیز ممکن است نتایج به دست آمده را تغییر دهد. کیش شخصیت (Cult of Personality) به کارتان نخواهد آمد هزاران پیشگام حوزه‌ی تجربه کاربری وجود دارد. امروزه دانش بعضی از افراد بسیار مد روز است ولی فردا نادیده گرفته خواهند شد یا برعکس. هیچ «راه صحیحی» برای پژوهشگر تجربه کاربری بودن، وجود ندارد. نام‌هایی که به ایده‌های تجربه کاربری داده شده‌اند را نادیده بگیرید و به جای آن روی ایده‌ی اصلی تمرکز کنید و با همه چیز با مقدار سازنده‌ی تردید و علاقه برخورد کنید. پی‌نوشت: منبع اصلی
  26. قاسم رمضانی منش

    با StyleSheet زیاد امتحان کرده‌ام آن خروجی‌ای که مدنظرم هست (و توی عکس‌بالا هم ارسال کرده‌ام) به دست نیومد.
  27. کامبیز اسدزاده

    از مشخصه‌ی setStyleSheet تحت CSS کار کن، مثال زیر رو ببین: QSlider::groove:horizontal { border: 1px solid #bbb; background: white; height: 10px; border-radius: 4px; } QSlider::sub-page:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #66e, stop: 1 #bbf); background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, stop: 0 #bbf, stop: 1 #55f); border: 1px solid #777; height: 10px; border-radius: 4px; } QSlider::add-page:horizontal { background: #fff; border: 1px solid #777; height: 10px; border-radius: 4px; } QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #eee, stop:1 #ccc); border: 1px solid #777; width: 13px; margin-top: -2px; margin-bottom: -2px; border-radius: 4px; } QSlider::handle:horizontal:hover { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #fff, stop:1 #ddd); border: 1px solid #444; border-radius: 4px; } QSlider::sub-page:horizontal:disabled { background: #bbb; border-color: #999; } QSlider::add-page:horizontal:disabled { background: #eee; border-color: #999; } QSlider::handle:horizontal:disabled { background: #eee; border: 1px solid #aaa; border-radius: 4px; } البته ساختن چنین مواردی رو من در QML پیشنهاد می‌کنم.
  1. نمایش فعالیت های بیشتر
×
×
  • جدید...