افتخار دوستی با خانوادهٔ StackExchange

احتمالا اکثرتان با Stackoverflow یا نهایتا با SuperUser آشنا هستید‌. اما این خانواده اعضای جالب‌تری هم دارد‌.

English

اگر مثل من یک کمی دیر به فکر تقویت زبان‌تان افتاده‌اید‌، ممکن است سوال‌هایی داشته باشید که جوابش در کتاب‌هایتان نبوده و جستجو در گوگل هم نتیجه‌ای برایتان به همراه نیاورد‌. آن وقت است که فضایی مثل یاران StackExchange به دادتان می‌رسد‌.

Tex

این یکی مخصوصا برای اولین تجربه از Tex و توله‌هایش خیلی خوب به کارتان می‌آید‌. قبلا در مورد تک صحبت کرده‌ام‌. اصولا آن ضرب‌المثل معروف کاربران LaTex (‌اولین بار که داری با لاتک مقاله‌ای آماده می‌کنی می‌گی دیگه سراغش نمی‌رم‌، ولی وقتی مقاله تموم شد‌، دیگه جز با لاتک کاری انجام نمی‌دی) توصیف کنندهٔ وضعیت فعلی من است و مقالات و نوشته‌هایی که با ابزار‌های دیگری نوشته شده‌اند هیچ گونه اشتیاقی برای استفاده در من ایجاد نمی‌کنند‌!‌ دیکتاتوری است دیگر D:

Ubuntu

چه دوست داشته باشم یا نه‌، اوبونتو طیف وسیعی از تازه‌کاران سوال کننده (و البته کهنه کاران پاسخ دهنده) را به سوی خود جذب می‌کند‌. این می‌شود که بخش Ubuntu از StackExchange به یکی از باحال‌ترین مکان‌ها برای پیدا کردن راه حل مشکلات عجیب غریب‌تان تبدیل می‌شود‌!

اگر شما هم مثل من Ubuntu را یک سیستم عامل خارق‌العادهٔ‌، متفاوت‌، از همهٔ لینوکس‌ها بهتر نمی‌دانید و تنها مثل هر توزیع دیگری و کاملا دموکراتیک به عنوان یک سیستم‌عامل لینوکسی/یونیکسی آن را در دستهٔ مشخصی قرار می‌دهید‌، Unix/Linux در StackExchange می‌تواند جای خوبی برای طرح سوال‌های عجیب غریبتان باشد‌! البته اگر سوالاتتان طیف وسیع‌تری را پوشش می‌دهد‌، می‌توانید SuperUser را امتحان کنید‌!

دیگران

خانوادهٔ StackExchange بزرگتر از این حرف‌هاست که بشود در یک مطلب به توضیح تک‌تکشان پرداخت‌! از طرفی من هم سواد بررسی همهٔ آن‌ها را ندارم تا در موردشان نقد و بررسی درست درمانی بنویسم‌! این می‌شود که پیشنهاد می‌کنم‌، برای آشنایی با آن‌ها یکی از سایت‌های هم‌خانواده‌ای را که معرفی کردم باز کنید و اسکرولتان را تا پایین صفحه بچرخانید و چشمتان را به جمال عجایبی همچون Wordpress, Photography, Teoreticalcs و حتی Cooking روشن کنید‌!

احتیاط

می‌دانم که عموم خوانندگان این مطلب را کاربرانی تشکیل می‌دهند که بلدند چطور سوال بپرسند و قبل از طرح کردن مساله‌ای به خوبی تمام جوانبش را بررسی می‌کنند و وقتی دیگر از گوگل و تمام منابع دم دستشان نا امید شدند به فکر سوال کردن در فرومی‌، جایی می‌افتند‌! اما اگر خدای نکرده کسی از این جمع یادش رفت و یکی از اصول چندگانهٔ طرح سوال را رعایت نکرد‌، بداند که تنها باعث منزوی شدن نام کاربری‌اش در جامعهٔ StackExchange می‌شود و این کلا چیز خوبی برای ادامهٔ حیات در آن جامعه نیست ;-)

پروژهٔ نگار و مصائبش

نگار با پایتون نوشته شده‌، اما به احتمال زیاد کتابخانهٔ ویرایشش به چند زبان دیگر (نظیر PHP و javascript برگردانده خواهد شد). از این رو نگار روی گیت‌هاب منتشر می‌شود و شدیدا پذیرای ایده‌های نو و صد البته همکاری شما در توسعه‌اش است‌.

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

مصائب توسعه نگار

آن‌هایی که مرا از نزدیک می‌شناسند احتمالا می‌دانند که مدت‌هاست مشتاقم که برنامه‌نویس شوم ولی درصدی تنبلی و درصدی دیگر پیچیدگی امر برنامه‌نویسی مانع از رسیدن به این علاقه‌ام شده‌. بار‌ها به سمت زبان‌های C‎+‎+‎‌، C، PHP، Javascript و ... کشیده شده‌ام‌، سینتکس‌شان را یادگرفته‌ام‌، تعدادی مسائل و الگوریتم پایه‌ای را حل کرده‌ام ولی آخر سر نتوانسته‌ام که به خودم اجازهٔ برنامه‌نویس بودن بدهم D:

خوبی این تلاش‌های به بن‌بست رسیده برایم این بوده که بفهمم برنامه‌نویسی را توی کتاب‌ها یاد نمی‌دهند D: (البته منظورم سینتکس و این‌ها نیست‌ها!). یاد گرفتن واقعی از وقتی شروع می‌شود که شروع به نوشتن پروژه‌ای می‌کنید‌. ایدهٔ توسعهٔ نگار را مدت زیادی بود که در سر می‌پروراندم‌. خصوصا از وقتی که ایمان ویراستیار را به عنوان جایگزینی برای ویراستباز که من استفاده می‌کردم معرفی کرد و گفت که ای کاش یک نسخهٔ کلاینت هم برای این کار روی لینوکس بود‌. (درست که ویراستیار یک نسخهٔ کلاینت دارد‌، ولی آن با زبان جهنمی C#‎ نوشته شده که نصبش برای من هفت خوان می‌طلبد!)

این اشتیاق به قوت خودش باقی ماند تا این که کتاب یک بایت از پایتون را تمام کردم‌. کتاب خیلی خوبی بود‌‌ و خیلی از مسائل را به سادگی و روشنی توضیح می‌داد‌. این بود که ما هم جو این که هر کاری را می‌توانیم به راحتی با پایتون انجام دهیم گرفتمان و زدیم به کار نوشتن نگار‌!

اولین الگوریتم پیاده‌سازی شده افتضاح بود‌! بعضی ایده‌هایش هنوز هم به نظرم جالب می‌آید ولی حجم کار بی‌خودی زیادی را به هر تابع واگذار کرده بودم که در نهایت هم نتیجهٔ دلچسبی در خروجی ظاهر نمی‌شد‌. این بود که کمی ذلسرد شدم و ناامیدانه به دنبال ابزاری گشتم که برای همین هدف نوشته شده باشد و بتوانم با بررسی سورسش مفهوم طراحی و پیاده‌سازی یک برنامه را بهتر درک کنم‌. سورس ویراستیار جدای از گندگی بیش از اندازه‌اش برای من‌، به زبانی بود که اصلا نمی‌فهمیدمش‌! جستجو‌ها نتجه نداد تا وقتی که روی صفحهٔ گیت بهنام اسفهبد با پروژهٔ ویراستار آشنا شدم‌. نویسندهٔ پروژه عزیز (که فکر کنم توسعه دهندهٔ بالاترین هم باشد) کد کتابخانه را به روبی نوشته بود و یک نمونهٔ تحت وب که خروجی فوق‌العاده‌ای هم از ویرایش متن داشت با آن ساخته بود‌.

سادگی و کامنت شدن بیشتر توابع این کتابخانه را به شروع خوبی برای من تبدیل کرد‌. تصمیم گرفتم که آن را با پایتون دوباره نویسی کنم تا هم سواد خودم را (که بعد‌تر فهمیدم اصلا ندارم D:) محک بزنم و هم یه شروع برای پروژهٔ نگار ایجاد کنم‌.

کار از آن چیزی که به نظر می‌رسید سخت‌تر بود‌. تفاوت‌های زبان روبی و پایتون گاها در سینتکس خیلی کوچک بودند ولی در عملکرد زمین تا آسمان متفاوت بودند‌.

نکته‌ای که بیش از همه به نظرم جالب آمد طرز تفکر من نسبت به عبارات باقائده بود‌. قبلا در وبلاگ دوست خوبم علی با آن‌ها آشنا شده بودم‌. ولی زیاد دل به یاد گرفتن‌شان ندادم و فکر هم نمی‌کردم آن‌چنان دانستن‌شان به کارم بیاید‌. حقیقتش را بخواهید بیش از ۹۰ درصد ویراستار نگار بر پایهٔ همین regex‌ها بنا شده ;-)

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

کمک‌های Stackoverflow و Github

این که می‌گویند همیشه ابزار درست نتیجه را بهتر می‌کند (نقل از خودم) حقیقت محض است‌. در حین نوشتن نگار به مشکلاتی بر می‌خوردم که هیچ‌جوره با منطقم جور در نمی‌آمد‌. سرچ در گوگل هم خصوصا این که با فارسی در ارتباط بود و طبیعتا نمی‌شود دلی به نتایجش خوش کرد چندان فایده‌ای نداشت‌ (البته چند باری به جای فارسی دنبال Arabic‌ها گشتم که راهنمایی‌های خوبی برای یونیکد نوشتن در پایتون به دست آوردم). در چنین شرایطی بود که stackoverflow و کاربرانش به دادم رسیدند‌. دو سوالی که پرسیدم را آن‌چنان کامل و متناسب با شرایط مختلف جواب دادند که عمرا نمی‌شود در هیچ کتابی به این صراحت جوابی پیدا کرد D:

اما حل مشکلات به این راحتی‌ها هم نبود‌. بار‌ها پیش می‌آمد که شروع به ویرایش یک تابع می‌کردم و آن‌قدر تغییر می‌دادم که در نهایت نه ایده‌هایم برای حل مشکل کارساز بود و هم کل کد را خراب کرده بودم‌. این‌جا بود که Git کمک می‌کرد‌. فوق‌العاده است‌. فوق‌العاده‌. کدام ابزار دیگری را می‌شناسید که هم یک ورژن کنترل عالی باشد‌، هم به راحتی قابلیت مستندسازی پروژه را دو دستی تقدیمتان کند؟ (منظورم wiki و page است). در آینده بیشتر در مورد شگفتی‌های گیت و مخصوصا گیت‌هاب می‌نویسم‌!

نتیجه:

نتیجهٔ کار که دیگر معلوم است‌. اما در آخر باز هم اعلام می‌کنم که اگر علاقه به همکاری در یک پروژهٔ آزاد فارسی دارید تنها زحمتی که باید به خودتان بدهید‌، این است که پروژه را از گیت‌هاب فورک کنید ;-)

از Autojump در ترمینال غافل نشوید‌!

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

نصب

برای نصب اگر پایتون روی سیستم‌تان بالا‌تر از ۲.۷ است که خیال‌تان راحت و تنها کافییست که آن را از کانال گیتش دریافت کنید و پس از ورود به پوشه‌اش اسکریپت install.sh را اجرا کنید‌. اما اگر مثل من با این ترافیک داغان اینترنت روی اسلکور استیبل گیر کرده‌اید (این یعنی پایتون ۲.۶) پس باید یک چند‌تا کار اضافه انجام دهید‌ (یا شاید هم بخواهید به ورژن ۱۹ که توی اسلک‌بیلد‌ها پیدا می‌شود بسنده کنید):

sbopkg -R -i "pysetuptools pip"

امیدوارم sbopkg را نصب کرده و بی‌خودی زندگی را برای خود سخت نگیرید ;-) حالا هم کافیست که با pip بستهٔ مورد نیاز Autojump را نصب کنید‌:

pip install argparse

و سپس:

git clone git://github.com/joelthelion/autojump.git

cd autojump

./install.sh --force

و حالا خط زیر را به ‎.zshrc اضافه کنید:

[[ -s /etc/profile.d/autojump.zsh ]] && source /etc/profile.d/autojump.zsh

و سر‌انجام:

source ~/.zshrc
مشکل

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

vim ~/.zsh[TAB]

که احتمالا چیزی شبیه خروجی زیر نصیبتان می‌شود:

_arguments:439: _vim_files: function definition file not found

_arguments:439: _vim_files: function definition file not found

_arguments:439: _vim_files: function definition file not found

و این یعنی اتوکامپلیشن فوق‌العادهٔ ZSH جلوی دستور vim از کار می‌افتد‌! خوب چه کنیم؟ تنها یک راه حل در اینترنت جواب این مساله بود و آن هم از انجمن آرچر‌های اعظم:

$ echo $fpath (see the function PATH of zsh)

/usr/share/zsh/site-functions /usr/share/zsh/4.3.11/functions

$ cd /usr/share/zsh/4.3.11/functions

$ ls _vi*

_vim

rm _vim

و حالا لذت ببرید ;-)

چگونه ایمکس‌مان را تربیت کنیم (قسمت دوم)

یکی از اجزای جدا نشدنی توابع برنامه نویسی‌، آرگومان‌هایی هستند که می‌توان با استقاده از آن‌ها کارکرد توابع را تحت تاثیر قرار داد‌. توابع ایمکس هم از این قائده مستثنا نیستند‌. برای مثال می‌توان با C-u 3 C-x C-n نشانگر را به سه پنجره بعد از پنجرهٔ حاضر منتقل کرد‌. می‌خواهید امتحان کنید؟ ایمکس‌تان را باز کنید و آن را به چهار پنجره تقسیم کنید و فرمان بالا را وارد کنید‌. همان‌طور که می‌بینید تابع other-window که در این‌جا مورد بحث ماست‌، با استفاده از آرگومان دریافتی توسط C-u که همان عدد سه است‌، می‌تواند به سه پنجره بعد بپرد و ما را از فراخوانی پیاپی‌اش نجات دهد‌. حالا نکته این‌جاست که این تابع آرگومان منفی‌ای مثل ‎-۱ را هم می‌پذیرد‌. و احتمالا این دقیقا همان چیزی است که ما برای حرکت در پنجره‌ها به سمت چپ نیاز داریم‌. یک بار امتحانش کنید: C-u -1 C-x C-n

نکته: اکثر توابع موجود در ایمکس با استفاده از مقادیری مثل ‎-۱ عکس عمل عادی‌شان را انجام می‌دهند‌. فکر کنید که با این ترکیب چه توابع جالب و کاربردی‌ای می‌توان ساخت‌. مثلا کلید delete عقب‌گرد که در ایمکس به طور عادی تعریف نشده D: (در آینده یک نمونه خواهیم ساخت ;-))

تعریف اولیه other-window-backward

برای این که از سختی وارد کردن همچون دستوری بکاهیم‌، بهتر است که آن را به صورت یک تابع تعریف کنیم تا هر جایی که نیاز بود بتوانیم به راحتی از آن استفاده کنیم:

(defun other-window-backward ( )

"Select the previous window."

(interactive)

(other-window -1))

خوب این خطوط به چه معناست؟ به ترتیب آن‌ها را بررسی می‌کنیم:

(defun other-window-backward ( )

در این خط مثل اکثر زبان‌های برنامه‌نویسی دیگر‌، شروع به تعریف تابع می‌کنیم‌. defun عبارتی است مانند def در پایتون که وظیفهٔ تعریف توابع را بر عهده دارد‌. در ادامه نیز اسم تابع other-window-backward و یک جفت پرانتز خالی (که محل تعریف آرگومان‌هایمان در آینده است) را قرار می‌دهیم‌.

در خط دوم:

"Select the previous window"

تنها یک توضیح کوتاه برای عملکرد تابع می‌نویسیم‌. این توضیح عملا تاثیری در روند اجرا تابع ندارد‌. اما وقتی کاربر بخواهد با دستوری مثل describe-function از نحوهٔ عملکرد تابع مطلع شود‌، این توضیح است که نمایش داده می‌شود‌.

خط سوم شامل:

(interactive)

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

و خط آخر:

(other-window -1))

همان‌طور که معلوم است‌، تابع other-window آشنایمان را با آرگومان ‎-۱ صدا می‌کند‌. این آرگومان منفی همانطور که توضیح دادیم به تابع می‌فهماند که باید عقب‌گرد حرکت کند‌.

حالا دیگر تقریبا کارمان تمام است‌. تنها کافی است که آن را به کلید ترکیبی مد نظرمان (C-x C-p چنان که در قسمت قبل تصمیم گرفتیم) پیوند دهیم:

(global-set-key "\C-x\C-p" 'other-window-backward)

و کار تقریبا تمام است‌.

افزودن مزایای آرگومان‌ها

تا این‌جای کار other-window-backward کاری که می‌خواهیم را درست انجام می‌دهد‌. اما هنوز یک کمبود دارد‌. کدام کمبود؟ همانطور که بالا‌تر توضیح دادم ما می‌توانستیم با C-u به other-window بگوییم که مثلا به دو پنجره بعد بپرد و ما را از فراخوانی دوباره و دوباره‌اش نجات دهد‌. اما تابعی که الان تعریف کردیم‌، این قابلیت را ندارد‌. در نتیجه کاربر بخت برگشته باید برای پرش به سه پنجره قبل سه بار این کلید‌ها را فشار دهد‌! و این یعنی عذاب ;-)

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

(defun other-window-backward (n)

"Select Nth previous window."

(interactive "p")

(other-window (- n)))

همانطور که می‌بینید در خط اول‌، یک آرگومان به لیست آرگومان‌های تابع به اسم n اضافه کردیم‌. با این کار به تابع فهماندیم اگر کاربر با C-u آرگومانی را فرستاد (مثلا ۲) آن را در متغیر n قرار بده‌.

همینطور در خط آخر به جای استفاده از ‎-۱ تابع other-window را با آرگومان منفی n صدا زدیم‌. یعنی هر عددی به n فرستاده شد‌، به ترکیب فوق‌، به صورت منفی به other-window فرستاده می‌شود‌.

توجه: به فاصلهٔ میان علامت منفی و حرف n توجه کنید‌. در صورتی که بخواهید آن را به صورت:

(-n)

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

(- 2 6)

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

اما یک نکتهٔ مهم و اساسی در خط سوم وجود دارد که باید به آن نیز بپردازیم‌. ما در این خط به صورت زیر عمل کردیم‌:

(interactive "p")

جریان آن p مشکوک چیست؟ اگر تابع مثل روش اول‌مان‌، هیچ آرگومانی نگیرد‌، interactive هم بدون آرگومان می‌ماند‌. اما اگر مثل روش اخیرمان بخواهیم در تابع از آرگومانی استفاده کنیم‌، باید یک آرگومان به صورت رشته کد به interactive هم اضافه کنیم‌. کد p کوچک به تابع می‌فهماند که اگر یک آرگومان به تابع فرستاده شد آن را به صورت یک عدد ترجمه کن و به تابع برگردان‌. اما اگر آرگومانی فرستاده نشد‌، خودت به صورت پیش‌فرض عدد ۱ را به تابع برگردان‌. و این دقیقا کاری است که ما تا این‌جای کار از تابع‌مان می‌خواهیم‌.

اختیاری کردن آرگومان

همان‌طور که می‌بینید‌، این تابع قابلیت فراخوانی توسط توابع دیگر لیسپ را نیز دارد‌. اما در این وضعیت (منظورم در حالتی است که تابع توسط توابع دیگر فراخوانده شود و نه در صورتی که توسط کاربر صدا زده می‌شوند) حتما باید به همراه فراخوانی تابع یک آرگومان هم به آن اطلاق شود‌. و این کمی کار را پیچیده می‌کند و ممکن است در آینده خسته کننده به نظر بیاید‌. بهتر است تابع را طوری تعریف کنیم که وقتی به صورت other-window-backward صدا شد خودکار روش انجام کار پیش‌فرضش را پیش بگیرد‌. پس آن را به صورت زیر ویرایش می‌کنیم:

(defun other-window-backward (&optional n)

"Select Nth previous window."

(interactive "p")

(if n

(other-window (- n)) ; if n is non-nil

(other-window -1))) ; if n is nil

در خط اول با ‏‎&optional به تابع می‌فهمانیم که آرگومان n باید اختیاری باشد‌. یعنی اگر فرستاده شد مقدار دهی شود و اگر نه‌، بی‌خیالش باشد‌. همچنین از یک عبارت کنترلی if هم استفاده کردیم‌. در این عبارت اگر n مقدار داشته باشد‌، پس if با یک وضعیت non-nil یا همان True در اکثر زبان‌های برنامه نویسی روبروست‌. و اگر مقدار نداشته باشد‌، برابر است با nil یا False. با توجه با کامنت‌های موجود در کد احتمالا می‌توانید بفهمید که در صورت وقوع هر یک از این وضعیت‌ها کدام حالت رخ خواهد داد‌.

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

پی‌نوشت: مرا به علت دیر‌کرد این چند وقته در انتشار ادامهٔ مطلب حاضر ببخشید‌. امروز هم احتمالا نتوانسته‌ام آن‌طور که باید و شاید حق مطلب رو عدا کنم‌. حقیقتش امروز از آن روز‌هایی بود که در مود نوشتن برای وبلاگ نبودم ولی باید حتما تا این‌جای کار را بیان می‌کردم تا نوشته‌ها از خودم عقب نمانند ;-)

چگونه ایمکس‌مان را تربیت کنیم (قسمت اول)

احتمالا هم دیگر معلوم است که چند وقتی است دارم عادت می‌کنم که با ایمکس کار کنم‌. شاید برای باقی ملت این هم جزء همان جو گرفتن‌های اولیه باشد‌. ولی این بار این‌طور نیست‌. موضوع ناموسی شده‌! بیشتر از تمام نرم‌افزار‌هایی که در طول عمرم با آن‌ها سر و کار داشته‌ام‌، دوست دارم با ایمکس کار کنم‌. ولی این یکی تمام دانسته‌هایم را‌، عادت‌هایم را‌، علایقم را‌، بود و نبودم را به چالش می‌کشد‌! چطور؟ در این‌جا با نرم‌افزاری طرف هستم که بسیاری چیز دارد که من نمی‌خواهم‌، و بسیاری چیز دیگر ندارد که من می‌خواهم‌، (که دقیقا عکس همین عبارت هم صادق است). حالا نکته‌اش این‌جاست که این نرم‌افزار این قابلیت را به من می‌دهد که تبدیلش کنم به همان چیزی که می‌خواهم‌! چطور؟ elisp‌!

در این مدت دو سه ماهه که با ایمکس سر و کله زده‌ام‌، منابعی یافته‌ام (که در پست بعدی سعی می‌کنم معرفی‌شان کنم) که کمکم کرده تا اندکی این زبان و تنظیمات (زیادزیادزیاد) ایمکس را درک کنم‌. همین درک اندک کلی هیجان هم برایم به همراه آورده‌. این است که می‌خواهم تا تمام شدن دوره کردن این منابعم‌، هر چیزی که به نظرم جالب و کاربردی می‌آید را تحت یک سری مقاله منتشر کنم‌. باشد که برای آیندگان مفید واقع شود ;-)

پیشنیاز

من این‌طور فرض می‌کنم که خوانندهٔ عادی این متن تا الان حداقل کلید‌های میانبر ایمکس را حفظ کرده‌، بلد است Frame کدام است‌، یا چطور بین پنجره‌ها جابجا شود یا پکیجی نصب کند‌! در این‌جا قرار است ما یاد بگیریم که چطور پکیج‌های خودمان را بسازیم ;-)

یک نکته

قرار نیست که در مورد هر دستوری که استفاده می‌کنم کامل توضیح دهم‌. گاهی می‌شود که تنها به یک توضیح کوتاه و نشان دادن عملکرد دستور بسنده می‌کنم‌. این‌طوری سعی دارم که بیشتر تمرکز را بگذارم روی هدف اصلی مطلب.

ساختار دستورات elisp

اولین نکته در مورد لیسپ (یا در این مورد elisp) این است که تمامی دستوراتش باید داخل پرانتز نوشته شود‌. یعنی فرمی به صورت زیر:

(command)

نکتهٔ بعد نوع نوشتن دستورات است‌. در زبان‌هایی مانند C یا Python یا دیگر زبان‌هایی که تا الان دیده بودم‌ (جز اسمبلی) دستورات تقریبا به صورت عامیانه نوشته می‌شد‌. مثلا ۲ + ۳ را دقیقا همینطور می‌نوشتیم‌. ولی در لیسپ باید این ترتیب رعایت شود:‌ ۱- عملگر ۲- آرگومان‌ها‌. یعنی برای دستور ۲ + ۳ داریم:

(+ 2 3)

خوب مزیتش می‌شود این که دیگر برای ۲ + ۳ + ۴ + ۵ + ۶ فقط می‌نویسیم:

(+ 2 3 4 5 6 )

و دیگر خبری هم از دردسر اولویت عملگر‌ها نمی‌شود‌. مثلا:

(+ 3 (* 4 5)) که جوابش می‌شود ۲۳

(* (+ 3 4) 5) که جوابش می‌شود ۳۵

یا مثلا اگر قرار بود قبلا تابعی را با آرگومان‌هایش به صورت زیر فرا بخوانیم:

foo (3 + 4, 5 + 6)

الان کارمان به این صورت انجام می‌گیرد:

(foo (+ 3 4) (+ 5 6))

ساده بود؟‌ خوب نکتهٔ‌ بعدی لیست‌ها هستند‌. که به صورت زیر تعریف می‌شوند:

(hello there) که لیست شامل دو نشانه است.

(1 2 xyz ") که لیستی شامل دو عدد و یک رشته است‌.

(a (b c)) که لیستی شامل یک نشانه و یک زیر لیست است‌.

() که یک لیست خالی است‌.

خوب تنها نکته‌ای که باقی می‌ماند و از باقی نکته‌ها مهم‌تر است کامنت است‌! در لیسپ کامنت‌ها بعد از «;» نوشته می‌شوند‌. هر وقت این علامت ظاهر شد تا انتهای خط به عنوان کامنت توسط مفسر لیسپ کامنت طلقی شده و نادیده گرفته می‌شود‌.

نکات پایه‌ای در مورد نحو لیسپ را تا این‌جای کار دیدیم‌. نکات ضریف‌تر و کاربردی‌تری هم وجود دارد که در ادامهٔ مقالات و وقتی به آن‌ها نیاز شد مطالعه‌شان می‌کنیم.

کلید‌ها در elisp

برای این که به ایمکس بفمانیم با فشرده شدن فلان کلید فلان کار را انجام بده‌، باید اول بدانیم که چطور یک کلید را تعریف کنیم‌. کلید‌ها در حالت عادی بین دو کتیشن "" و به صورت یک رشته تعریف می‌شوند‌. مثلا در مثلا زیر ما سه کلید xyz را تعریف می‌کنیم:

"xyz"

اما نکته این‌جاست که چطور کلید‌های Ctrl و Alt را به ایمکس حالی کنیم‌! این کلید‌ها عملا با حرف C بزرگ برای Ctrl و M برای کلید Meta یا همان Alt شناخته می‌شوند‌. اما اگر ما "M" یا "C" استفاده کنیم، حروف M بزرگ و C بزرگ را معرفی کرده‌ایم و نه Ctrl و Alt را‌. این‌جاست که از علامت شکنندهٔ «\» استفاده می‌کنیم‌. (اگر قبلا برنامه نویسی کرده باشید احتمالا خوب از مزایایش با خبرید‌). و داریم:

"\M"

خوب دیگر کارمان راحت شد‌. فقط یک نکتهٔ کوچک دیگر باقی می‌ماند‌. این که چطور فرق بین پایین نگه داشتن کلید Ctrl و زدن x و بعد رها کردن هر دویشان و زدن مثلا کلید l را تعیین کنیم‌. این عبارت در ایمکس به صورت C-x l نوشته می‌شود‌. خوب احتمالا حدس زده‌اید‌:

"\C-x l"

نکته: برای نمایش کلید Esc به صورت "\e" و برای کلید Del از عبارت "\C-‎?‎" استفاده می‌کنیم.

امتحان تغییرات ایجاد شده روی ایمکس

خوب ما تغییرات و توابع ایجاد شده‌یمان را در فایل ‎.emacs موجود در پوشهٔ خانگیمان وارد می‌کنیم‌. اما چطور تغییرات را اعمال کنیم؟ ساده‌ترین راه و اولین چیزی که احتمالا به ذهنتان می‌رسد بستن ایمکس و باز کردن دوباره‌اش است. روش خوبی است‌‌، ولی آسان‌تر از این هم پیدا می‌شود‌. مثلا زدن دستور زیر:

M-x eval-buffer RET

این دستور تقریبا کار راه انداز است‌. ولی موقعی که فایل کانفیگتان زیادی سنگین شد‌، و مثلا رسید به هزار خط (‌می‌رسد‌، غم مخورید ;-)) ممکن است زمان زیادی تا لود شدن طول بکشد‌. پس منطقی نیست که همهٔ فایل را لود کنیم‌. چطور است که فقط تغییرات ایجاد شده را دوباره لود کنیم؟ (همین انتخاب‌های گسترده است که دارد عاشق دل باخته‌ام می‌کند ;-)) پس راه بعدی‌مان می‌شود این که برویم در انتهای خطی که تغییر جدیدمان را اعمال کرده‌ایم بایستیم و کلید‌های زیر را وارد کنیم:

C-x C-e

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

خوب ما می‌دانیم که این کلید‌ها حتما یک دستوری را پشت پردهٔ ایمکس اجرا می‌کنند‌. می‌خواهید بدانید که آن دستور چیست؟ اول دستور زیر را وارد کنید:

M-x describe-key

و سپس کلید‌های ترکیبی‌تان را وارد کنید‌. یک توضیح کامل از دستور مورد استفاده در پنجره‌ای جدید روبرویتان ظاهر می‌شود‌.

خوب دیگر بحث تئوری کافیست‌. بهتر است برویم سراغ کمی کار عملی‌.

حل مشکل پرش بین پنجره‌های ایمکس

مساله خیلی ساده است‌. تا به حال حتما پیش آمده که چند تا پنجرهٔ باز دارید و می‌خواهید بین‌شان سوئیچ کرده و به پنجرهٔ سمت راست‌تان بروید. خوب برای این کار از کلید‌های C-x o استفاده می‌کنید‌. حالا اگر بخواهید به سمت چپ بروید چطور؟ شاید وقتی دو پنجرهٔ باز دارید کار سختی نباشد و با تکرار همین دستور به سمت چپی هدایت شوید‌. ولی در پنج پنجرهٔ باز چطور؟ چهار بار باید این کار را انجام دهید‌. یا شاید اصلا نظرتان به نظر من نزدیک باشد که C-x o یک کمی را دست نباشد‌. (این نظر شخصیست) طی ادامهٔ این شماره از مقاله و شمارهٔ بعد قصد حل این مساله را داریم‌. یعنی اول این که کلید‌های C-x o که وظیفه‌شان پرش به پنجرهٔ بعدی است را با یک کلید دلخواه جایگزین کنیم‌. و سپس یک کلید که بتواند عکس همین عمل را یعنی پرش به پنجرهٔ سمت چپ را انجام دهد هم بسازیم‌.

خوب تا این‌جای کار می‌دانیم چه کار‌هایی می‌توان انجام داد و می‌دانیم که چه مساله‌ای پیش رو داریم‌. پس حل تقریبا آسان است‌. پس شروع می‌کنیم‌. اولین کاری که باید بکنیم این است که بفهمیم با چه دستوری می‌توان یک کلید میانبر تعریف کرد‌. دستور مورد نظر ما global-set-key نام دارد‌. که به صورت زیر می‌تواند مورد استفاده قرار گیرد:

(global-set-key KEY COMMAND)

خوب حالا دو چیز بعدی که نیاز داریم هم سر و کله‌شان پیدا شد‌. اولی KEY که منظور کلید‌های میانبر مورد نظرمان است‌، و دیگری COMMAND که دستوری است که می‌خواهیم با زدن کلید‌های KEY اجرا شود‌. خوب چه کلید‌هایی را انتخاب کنیم؟ این کتابی که من به عنوان راهنما استفاده می‌کنم کلید‌های C-x C-n را برای پرش به پنجرهٔ سمت راست و C-x C-p را برای پرش به پنجرهٔ سمت چپ انتخاب کرده‌. انتخاب جالبی به نظر می‌رسد و من هم همان‌ها را انتخاب می‌کنم‌. اما نکته این‌جاست که اول ببینیم نکند یادمان نباشد و بزنیم یکی از کلید‌های میانبر حاضرمان را ناکار کنیم‌. پس می‌آییم و با describe-key ته و توی قضیه را می‌ریزیم روی دایره‌. هاها بعله‌، این کلید‌ها به ترتیب برای فراخوانی set-goal-column و mark-page استفاده می‌شوند‌. که خوب حتی روحم هم از وجود چنین چیز‌هایی خبر‌دار نبود‌. پس فکر نمی‌کنم که این تغییر اشکالی داشته باشد‌. ضمن این که هر وقت که بخواهم می‌توانم دستورام را بعد از M-x وارد و اجرا کنم D:. پس کلید‌ها هم تعیین شد و تا اینجای کار دستور مربوط به پرشمان به پنجرهٔ سمت راست چیزی شبیه به این شده:

(global-set-key "\C-x\C-n" COMMAND)

و حالا بخش آخر داستان‌. حتما دیگر یاد گرفته‌اید که چطور دستور مربوط به C-x o را که می‌خواهیم به C-x C-n پیوند بدهیم را پیدا کنید‌. ما پیدایش کردیم و فهمیدیم که دستوری به اسم other-window را اجرا می‌کند‌. پس احتمالا می‌گویید که دستورمان بعد از تکامل می‌شود این:

(global-set-key "\C-x\C-n" other-window)

خوب تا حدودی راست گفته‌اید‌. ولی مشکل اینجاست که لیسپ کمی خنگ است و آن دستوری که سمت راست نوشته‌ایم other-window را هم اجرا می‌کند و این اصلا خوب نیست‌. پس باید یک جوری حالیش کنیم که بی‌خیال این دستور شود و فقط سمت چپی را اجرا کند‌. این است که یک «'» به تنهایی به کمکمان می‌آید و دستور می‌شود:

(global-set-key "\C-x\C-n" 'other-window)

همین کتیشن کوچک در ابتدای دستور از اجرای دستور دوم جلوگیری می‌کند و دستورمان کامل می‌شود‌. حالا می‌ماند که چطور به ایمکس بگوییم که از آن استفاده کند‌. خوب قبلا در موردش توضیح دادم‌. مثلا همین الان بروی به آخر خط همین دستور (C-e) و بزنید C-x C-e و تمام‌. حالا دستورتان را امتحان کنید‌.

چیزی که باقی می‌ماند این است که کلید C-x C-p را تعریف کنیم تا پس از زدنشان به پنجرهٔ قبلی یا سمت چپی‌مان برود و چون این کار یک کمی پیچیده‌تر است بهتر است آن را برای قسمت بعد بگذاریم‌. قسمتی که شما تا این‌جای داستان را به درستی درک کرده‌اید ;-)