LINUX.ORG.RU

Старая, добрая рекурсия но в labels

 ,


0

2
(defvar *result*)
(setq *result* 0)
(defun sum-list (lst)
  (if (eq (car lst) nil)
      *result*
    (progn
      (setq *result* (+ *result* (car lst)))
      (sum-list (cdr lst)))))

(sum-list '(1 2 3 4 5)) => 15

переписал, что-не доходит с labels. Никак не могу понять как она создаёт место в памяти и суммирует туда

(defun labels-sum-list (lst n)
  (labels ((temp (lst)
             (if (eq (car lst) nil)
                 n
               (progn
                 (setq n (+ n (car lst)))))))
    (temp (cdr lst))))

(labels-sum-list '(1 2 3 4 5) 0) => 2

Кто-то может подправить код, растолковать работу labels.

labels - это просто локальные функции которые могут ссылаться друг на друга (в отличие от flet). В варианте defun был рекурсивный вызов в последней строке. Куда в варианте labels делся?

Ну и результат в рекурсивных алгоритмах принято передавать не через глобальную переменную, а через аргумент-аккумулятор. Чуть позже код покажу.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik
(defun labels-sum-list (lst n)
  (labels ((temp (lst)
             (if (eq (car lst) nil)
                 n
               (progn
                 (setq n (+ n (car lst)))
    (temp (cdr lst))))))))  ;; теперь он рекурсивный и не работает, где-то косяк

(labels-sum-list '(1 2 3 4 5) 0) => nil
saufesma
() автор топика
Последнее исправление: saufesma (всего исправлений: 1)
Ответ на: комментарий от saufesma

Например, так.

(defun labels-sum-list (lst &optional (n 0))
  (labels ((temp (lst)
             (if (null (car lst))
                 n
                 (progn
                   (setq n (+ n (car lst)))
                   (temp (cdr lst))))))
    (temp lst)))

n - первоначальное значение аккумулятора. Логично что там при вызове должен быть 0.

(null ...) - это буквально (eq nil ...)

Первый вызов (в последней строке) выполняется со всем суммируемым списком, не пропуская первый элемент.

Последующие вызовы (предпоследняя строка, внутри локальной функции temp) - с остатком списка.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik

Ещё ошибка. Надо (null lst), а не (null (car lst)), иначе суммирование заглохнет на первом nil значении в списке. Хотя, оно и так заглохнет из-за численного +. А канонично рекурсивное суммирование делать как-то так:

(defun sum-list (lst &optional (n 0))
  (if (null lst)
      n
      (progn
        (incf n (car lst))
        (sum-list (cdr lst) n))))
Gentooshnik ★★★★★
()
Последнее исправление: Gentooshnik (всего исправлений: 1)
Ответ на: комментарий от Gentooshnik

Первый вызов (в последней строке) выполняется со всем суммируемым списком, не пропуская первый элемент.

(temp lst) находится за телом labels но вызывается первой, так, а само тело рекурсивное, так. А без этого хвостика у ьуня список прокручивалс и выплёвывал nil

(defun labels-sum-list (lst n)
  (labels ((temp (lst)
             (if (eq (car lst) nil)
                 n
               (progn
                 (setq n (+ n (car lst)))
    (temp (cdr lst))))))))

(labels-sum-list '(1 2 3 4 5) 0) =>nil

точнее в lst не было ничего, ведь (temp lst) и есть то место которое берет аргумент из определения функции и передаёт в тело labels, спасибо.

saufesma
() автор топика
Ответ на: комментарий от Forum0888

Посчитай суммарное количество разных скобок на других языках. Результаты будут примерно такие же, если нет значимых отступов как в Python или CoffeeScript.

Иногда вообще до абсурда доходит. Как на практике делаются короткие ассоциативные массивы на примитивах в языках где есть типы вроде «символ» или «атом» (т.е. ключи можно просто сравнивать по identity, без хэширования):

Common Lisp

(:one 1 :two 2 :three 3)

Erlang

{{one, 1}, {two, 2}, {three, 3}}

Конечно, это особенности стандартной библиотеки. Могли бы и так:

{one, 1, two, 2, three, 3}

Но тогда, из-за запятых, глаз с бОльшим трудом отличает ключ от значения. Потому что запятые сразу намекают на одноранговый список.

Много запятых в синтаксисе - ещё большее зло чем много скобок.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik

Как-то был на коммерческом проекте у компании которая купила лицензию на LispWorks, но они CAPI тоже не использовали.

А можешь в двух словах обрисовать проект. Я просто не верю, что у нас где-то делаются проекты по софту, все иностранное.

saufesma
() автор топика
Ответ на: комментарий от monk

Значимые пробелы - ещё большее зло чем запятые. >_> Они вдобавок ещё и затрудняют анализ кода всевозможными IDE. Просто сравни насколько «умно» пытается угадать вложенность какой-нибудь PyCharm (Python - основной язык для работы за деньги у меня сейчас), и насколько нет проблем с paredit.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik

Они вдобавок ещё и затрудняют анализ кода всевозможными IDE.

Для компьютерного парсера-то в чём проблема? Всё равно приходится учитывать разбивку по строкам для учёта комментариев.

monk ★★★★★
()
Ответ на: комментарий от Gentooshnik

Тут такое дело, мне с Lispworks support обзор наших с тобой кодов пришел, не лестный

I think the style used in your sum-list and labels-sum-list examples is not good because it mixes side-effects (i.e. setq) with recursion for no good reason. The cleaner, standard way to do this is to add on return from the recursive step:

(defun sum-list-1 (lst)
  (if (eq (car lst) nil)
      0
    (+ (car lst) (sum-list-1 (cdr lst)))))

or pass the sum as an extra argument:

(defun sum-list-2 (lst &optional (n 0))
  (if (eq (car lst) nil)
      n
    (sum-list-2 (cdr lst) (+ (car lst) n))))

а это наша версия

(defvar *result*)
 (setq *result* 0)
 (defun sum-list (lst)
   (if (eq (car lst) nil)
       *result*
     (progn
       (setq *result* (+ *result* (car lst)))
       (sum-list (cdr lst)))))
 
 (sum-list '(1 2 3 4 5)) => 15

 ;; I rewrite the above code using labels.
 
 (defun labels-sum-list (lst &optional (n 0))
   (labels ((temp (lst)
              (if (null (car lst))
                  n
                  (progn
                    (setq n (+ n (car lst)))
                    (temp (cdr lst))))))
     (temp lst)))

Вот видишь век живи век учись, я вот так переписал

(defun labels-sum-list (lst &optional (n 0))
   (labels ((temp (lst n)
              (if (null (car lst))
                  n
                  (progn
                    (temp (cdr lst) (+ n (car lst)))))))
     (temp lst n)))

(labels-sum-list '(1 2 3 4 5) 0)

saufesma
() автор топика
Последнее исправление: saufesma (всего исправлений: 1)
Ответ на: комментарий от saufesma

The cleaner, standard way to do this is to add on return from the recursive step:

Нет, потому что рекурсивный вызов тогда нехвостовой.

or pass the sum as an extra argument:

Я бы сказал что для пользователя этот n не имеет смысла. Мой вариант:

(defun labels-sum-list (lst)
   (labels ((temp (lst n)
              (if (null lst)
                  n
                  (temp (cdr lst) (+ n (car lst))))))
     (temp lst 0)))
Gentooshnik ★★★★★
()
Ответ на: комментарий от saufesma

А на примере кода можешь пояснить?

Пример нехвостовой рекурсии тебе саппорт написал.

(defun sum (...)
  (+ (sum ...) ...))

Хвостовая:

(defun sum (...)
  (sum (+ ...) ...))

Макросы с implicit progn’ами вроде let и labels на хвостовость рекурсии не влияют.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik

Да ладно чисто линейная рекурсия с великой книге о Лиспе SICP

(define (factorial n)
(if (= n 1)
1
(* n (factorial (- n 1)))))

одинаково

(defun sum-list-1 (lst)
  (if (eq (car lst) nil)
      0
    (+ (car lst) (sum-list-1 (cdr lst)))))

saufesma
() автор топика
Ответ на: комментарий от saufesma

Оптимизацию хвостовой рекурсии можно применить если рекурсивный вызов целиком является возвращаемым из функции значением. В примере из SICP результат рекурсивного вызова передаётся ещё в функцию * - это действительно линейная рекурсия. Линейная рекурсия не оптимизируется.

Gentooshnik ★★★★★
()
Ответ на: комментарий от Gentooshnik

Ага. Но когда пытаешься работать с вложенными структурами (хотя бы список списков словарей) в tcl всё печально.

Там базовой структурой является линейная строка, а не список элементов. Так-то ещё powershell можно вспомнить.

monk ★★★★★
()