Удобная упаковка для хвостовых рекурсивных функций - PullRequest
1 голос
/ 09 января 2020

Я пытаюсь выучить общий язык. Я знаком с хвостовой рекурсией, но я не знаком с идиоматическим c способом обернуть хвостовую рекурсивную функцию таким образом, чтобы вызывающая сторона не должна была инициализировать переменную (и) аккумулятора. Вот пример:

(defun add-em (n s) 
    (if (eql n 0) 
        s 
        (add-em (- n 1) (+ s n))
    )
) 

Скажем, я хотел обернуть эту функцию так, чтобы пользователю приходилось только управлять вводом n и не нуждался в полном вызове функции (add-em <number> 0). В других языках, таких как scala, я определяю внутреннюю функцию, а затем в конце внешней функции я вызываю хвостовую рекурсивную внутреннюю функцию для запуска алгоритма.

В общем lisp я мог бы определить лямбду в функции и использовать это, но это выглядит некрасиво. Я подумал, что может быть более идиоматический c способ сделать это, но поиск в Google на самом деле не дал мне никаких результатов.

Есть ли более идиоматический c способ сделать это, кроме полного разделения функций ? Или это лучший способ? Пример:

(defun add-em-inner (num sum)
       (if (eql num 0)
       sum
       (add-em-inner (- num 1) (+ num sum))
       )
)

(defun add-em (n) 
  (add-em-inner n 0)
)

1 Ответ

3 голосов
/ 09 января 2020

Одним из способов является использование оператора labels для определения рекурсивной лексической функции. То есть:

(defun add-em-inner (num sum)
       (if (eql num 0)
           sum
           (add-em-inner (- num 1) (+ num sum))))

(defun add-em (n) 
  (add-em-inner n 0))

Получается так:

(defun add-em (n) 
  (labels ((add-em-inner (num sum)
             (if (eql num 0)
               sum
               (add-em-inner (- num 1) (+ num sum)))))
    (add-em-inner n 0)))

Если вы не возражаете против того, чтобы дополнительный аккумулятор был частью интерфейса функции publi c, но заботитесь только что касается удобства пользователя (вызывающей стороне не нужно указывать значение), вы можете просто сделать его необязательным параметром:

(defun add-em (n &optional (s 0)) 
    (if (eql n 0) 
        s 
        (add-em (- n 1) (+ s n)))) 

Часто бывают веские причины не делать этого; например, вы можете захотеть сохранить возможность определения необязательного аргумента для будущего расширения API, которое обратно совместимо. Это все еще возможно здесь, но только при условии, что внешние абоненты не передают этот параметр.

...