معادل بازگشتی FOR loop

سلام به همه. کسی میتونه نحوۀ تبدیل حلقۀ for به یه تابع بازگشتی و پارامترهای مورد نیازش رو برام خیلی ساده و با مثال توضیح بده؟ یچیزایی خوندم ولی دنبال فرمول کلیش هستم که هر جا خواستم اینکارو انجام بدم.
حلقۀ for در واقع یه حالت خاص از توابع بازگشتیه که یه حالت ابتدایی یا اولیه دارن و جایی متوقف میشه بازگشتشون.
حلقۀ for ای هم که مد نظرمه اینه که در زبان پایتان مینویسمش:
for i in range(0, 9, 1)

چیزی که شما دنبالش هستید بخشی از functional programming هست.
شاید بهتر باشه توی یک زبان functional دنبالش بگردید نه پایتون! پایتون برای recursion مناسب نیست و وقتی تعداد بازگشتهامون از یه حدی بالاتر بره هم ارور میده! به هر حال:

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

def my_fn (num, limit=10):
    if (num < limit):
        print(num)
        my_fn(num+1, limit)

my_fn (1)

در نهایت باید بگم اینکار توی پایتون زیاد خوب نیست و بهتره با همون for each انجامش بدید.

2 Likes

نکته ای که وجود داره اینه که پایتون tail recursion و بهینه نمیکنه و خیلی توی این زمینه جالب عمل نمیکنه. البته میشه recursion limit و بالا برد اما کار درستی نیست.

2 Likes

@pouya-abbassi جان‌ جهت اطلاع کد شما base case نداره و کامل نیست، رها کردن ریکرژن به این امید که به null خطم بشه خیلی خوب نیست و دوم اینکه باید سعی کنیم کلا از null دوری کنیم، هرچند میدونم این فقط یک مثال بود اما نتونستم نگم :cold_face:

4 Likes

من دنبال یه تابع هستم به این صورت:
تابع من(عددابتدایی، عددانتهایی، اندازۀقدم شمارش، نام تابعی که باید در هر حلقه فراخونده بشه، پارامترهای تابع مورد نظر)
که کدش بصورت بازگشتی نوشته شده باشه.
به همون زبانهای فانکشنال یا لیسپ اگه باشه که بهتر، مثال بزنین.
فک کنم اون موقع اینجوری بشه:
image

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

کلوژر لوپ نداره.
https://clojuredocs.org/clojure.core/recur

اصلاح میکنم. لوپ داره ولی اونم recur داره.

(loop [x 10]
  (when (> x 1)
    (println x)
    (recur (- x 2))))
1 Like

این loop در کلوژر برای bind کردنه و فقط یک تشابه اسمی با loop های دیگه داره. قسمت بازگشتی recur هست که در مثال شما loop در نقطه بازگشت قرار گرفته اما کارش فقط بایند کردن x با 10 هست

2 Likes

به خاطر همین گفتم اینم recur میکنه.

در حقیقت به لوپ اینطور نگاه میکنم که یه scope هست که بتونیم توش tail call داشته باشیم و محدودش برای recur پیدا باشه. یعنی بدونه که کجا باید بپره.

2 Likes

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

2 Likes

اینم اضافه کنم که توی کلوژر for هم داریم.
ولی اونم قضیش با چیزی که از اسمش پیداست فرق میکنه. توضیحش یه کم داستان داره (یه macro هست) ولی درکل چیزیش که به بحث ما مربوط میشه اینه که اینم تبدیل به یه چیز دیگه میشه که داخلش recur داره.

1 Like

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

خب چیزی که شما میخواید مربوط به پایتون میشه.
توی پایتون نباید مسائل به این روش حل بشه.

خوندن لینکهایی که فرستادم هم به تنهایی کمکی به شما نمیکنه. چون قبلش باید با یه سری مسائل ابتداییتر #clojure آشنا بشید.
البته اصلا ترسناک نیست. به عنوان کسی که یک ساله با کلوژر آشنا شدم و الآن دارم اولین پروژه‌ی رسمی خودمو توی کلوژر مینویسم، حس میکنم پایتون خیلی سخت و مسخرست! (بگذریم از بقیه‌ی زبانها :sweat_smile:) اصلا نمیدونم چطوری یه زمان توی پایتون اینهمه پروژه ساختم!

خلاصه اینکه:

1 Like

چه جالب بود این تصویره! خیلی وقتا همینطوریه واقعا.
یزره لیسپ بلدم، خود کلوژر رو نه. ولی اگه شد با همون کلوژر یه تابع به اینصورت برام مثال بزنین:
image
که f2 اسم تابعیه که تو هر تکرار حلقه اجرا میشه و بهمراه پارامترای مربوط بهش تو یه لیست قرار میگیره و خود اون لیست هم کلش یکی از پارامترای یه تابع بازگشتیه که همون f1 میشه
یا شایدم ضروریه که دو تا تابع مجزا باشن و غیرممکنه دومی پارامتری از اولی باشه؟!

من الآن متوجه نمیشم این عکسی که شما اینجا گذاشتید یعنی چی!
این کد لیسپ نیست. صرفا یه چیزیه که توی پرانتز گذاشته شده :sweat_smile:
اگه شما معادل اینو میخواید:

 for i in range(0, 9, 1)

میتونم کمک کنم. ولی این عکس رو اصلا نمیفهمم!

1 Like

اینم اضافه کنم که کلوژر یکی از گویشهای لیسپ هست و شبیه همه‌ی لیسپهاست. فرق زیادی نمیکنه و یادگرفتنش برای کسی که یه لیسپ بلد باشه اصلا کاری نداره.
یه سری تفاوتها توی بطن ماجرا هست و یه فرق کوچیک توی سینتکس تعریف توابع. فکر نکنم فرق دیگه‌ای داشته باشه.
من الآن با کلوژر برنامه نویسی میکنم و فکر میکنم بتونم بدون مشکل با elisp هم کد بزنم. (کدهای ایمکس رو که نگاه میکنم کامل میفهممشون)

1 Like

نحوه فراخوندن تابعی که مد نظرمه به این شکله تو لیسپ. اما من دنبال تعریف و کدش هستم که بلدش نیستم
مثل همون
(+ 1 2)

خب شما اینجا تابع f1 رو صدا زدید و بهش ۳تا عدد فرستادید به همراه خروجی تابع f2!
یعنی به f1 در حقیقت ۴تا ورودی دادید.

توی این کد، اول f2 اجرا میشه، بعد جوابش کنار first و last و step به f1 فرستاده میشه.

1 Like

منم مطمئن نیستم نحوه قرار گرفتن پارامترا صحیح باشه :blush: فک کنم باید تابع دوم و پارامتراش از لیست در بیان و مجزا نوشته بشن

1 Like
(defn my-fn1 [stop step]
  (loop [start 0]               ;; Bind `start` to zero
    (when (< start stop)        ;; If `start` is smaller than `stop`
      (println start)           ;; Just a side effect
      (recur (+ start step))))) ;; GOTO `loop` line and start over with new `start`

(my-fn1 10 2)


(defn my-fn2 [start stop step]
  (when (< start stop)                 ;; If `start` is smaller than `stop`
    (println start)                    ;; Side effect
    (recur (+ start step) stop step))) ;; Goto `while` and start over with new numbers.

(my-fn2 0 10 2)


;; Another (better) way to do that.
(filter even? (range 10))

;; More complex than the line above.
(filter #(= (rem % 3) 0) (range 10))

;; just linke the above line, but drop the filst 10 value and only give me the rest
(drop 10 (filter #(= (rem % 3) 0) (range 50)))

خلاصه راههای رسیدن به خدا به عدد آدمهاست و صرفا جهت اطلاع باید بگم که استفاده از تابع، اینجا اصلا خوبی نیست. هم الکی biolerplate بیشتری تولید میشه، هم برای گرفتن خروجی (ما اینجا داریم فقط print میکنیم. خروجی رو return نمیکنیم) یه کم باید از اینی که هست هم پیچیده تر بشه.

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

0
2
4
6
8
nil

خروجی خطهای مربوط به filter این هست (که پرینت نمیشه و return میشه. اگه بخوایم پرینت بشه باید نتیجشو بفرستیم به تابع println):

(0 2 4 6 8)

(0 3 6 9)

(30 33 36 39 42 45 48)

بعد از ;; هرچی هست کامنته.

1 Like