Форум программистов, компьютерный форум, киберфорум
Lisp
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.75/12: Рейтинг темы: голосов - 12, средняя оценка - 4.75
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
1

Рекурсия, IF, новичок

10.04.2013, 15:03. Показов 2444. Ответов 32
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Lisp
1
2
3
(defun add(e l) (append l (list e))) 
(defun check(x) (cond ((eq x 2) (true)) ((> x 1) (loop for i from 2 to (+ (sqrt x) 1) do (if (eq (mod x i) 0) (return (false))) (return (true)))))) 
(defun nums(n l x) ((if (check x) (add x l)) (if (eq x n) (l) (nums n l (+ x 1)))))
add(e l) - добавляет в список l элемент e
check(x) - проверяет, простое ли число x
nums(n l x) - рекурсивная функция, составляющая список l простых чисел до указанного n, начиная проверку с x

Почему (nums 30 (list) 1) не работает?

P.S.: по учебе пришлось столкнуться с этим языком. Преподом предложена среда LispWorks. Ужасная кривая штука. Может попутно посоветуете ide?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.04.2013, 15:03
Ответы с готовыми решениями:

Новичок в питоне, но не новичок в программирование
Привет всем! У меня есть много вопросов, и может кто-то сможет ответить на несколько из них. Я...

Новичок.
Дорогие форумчане.... Я вообще нуб в раскрутке сайтов... Хочу научиться. Но не знаю с чего...

Новичок...
Привет всем, кто нидубь может придумать мне какое нибудь не трудное задание на HTML и CSS, и...

новичок
Ребят подскажите неопытному юзеру. Как всегда при наличии огромного желания вот да чего-то и...

32
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
10.04.2013, 15:35 2
Цитата Сообщение от mcmishok Посмотреть сообщение
Может попутно посоветуете ide?
Emacs (slime, paredit).
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
10.04.2013, 15:41 3
Цитата Сообщение от mcmishok Посмотреть сообщение
Преподом предложена среда LispWorks. Ужасная кривая штука. Может попутно посоветуете ide?
Стандартном является Emacs + SLIME, в частности, в виде Lisp in the Box. Всё можно поставить на винде. Я пользуюсь vim + slimv.

Если бы вы проверяли каждую функцию, REPL очень ругался бы уже на check.

Во-первых, в Лиспе истина и ложь суть t и nil соответственно. Вы, конечно, можете использовать символы true и false, но тогда заквотить их: 'true, 'false. Хотя в данном случае без надобности.

Во-вторых, как только вы написали (true), (false), Лисп пытается вызвать функции true и false с нулевыми аргументами. Это дефолтное обхождение со списком: первый элемент считается именем функции, остальное - аргументами.

В-третьих, ваша функция почти для всех x хочет проверить, будет ли число составным.

Идиоматический способ построения списков с нуля (то есть с nil) - добавлять элементы в голову с помощью push, а потом деструктивно разворачивать с помощью nreverse. append в цикле дорого обходится.

Подробнее - вечером. Если интересно, я тоже недавно с простыми числами баловался:
https://www.cyberforum.ru/post4310051.html
0
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
10.04.2013, 15:59  [ТС] 4
Если бы вы проверяли каждую функцию, REPL очень ругался бы уже на check.
Но функция работает.

ваша функция почти для всех x хочет проверить, будет ли число составным.
Для всех от 3 до n включительно.


При выполнении ошибка
Illegal argument in functor position: (IF (CHECK X) (ADD X L)) in ((IF (CHECK X) (ADD X L)) (IF (EQ X N) (L) (NUMS N L #)))
Не пойму, чем вызвана. ведь, если подставить числа, то все работает - (if (check 7) (add 7 (list)))
0
Модератор
Эксперт функциональных языков программированияЭксперт Python
36601 / 20330 / 4220
Регистрация: 12.02.2012
Сообщений: 33,643
Записей в блоге: 13
10.04.2013, 16:17 5
Много ошибок... Поэтому и не работает. А вот так:

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defun check(x) 
    (cond ((eq x 2) t) 
          ((> x 1) (not (iter (for i from 2 to (flo2fix (+ (sqr x) 1)))
                              (when (and (<> x i) (= (% x i) 0)) (return t)) ))))) 
 
==> check
 
(defun nums(n l x) 
 (cond ((= x n) l)
       ((check x) (nums n (cons x l) (+ x 1)))
       (t (nums n l (+ x 1)))))
 
==> nums
 
(nums 30 nil 1)
 
==> (29 23 19 17 13 11 7 5 3 2)
вполне работоспособно. Правда, для HomeLisp. Для Common Lisp нужно чуть подправить.

Добавлено через 7 минут
Исходная функция nums ошибочна:

Lisp
1
2
3
(defun nums(n l x) 
  ((if (check x) (add x l)) ;; лишняя левая скобка
   (if (eq x n) (l) (nums n l (+ x 1)))))
Самая же главная ошибка, на мой взгляд, в том, что вызов (add x l) возвращает список с добавленным x, но исходный l не меняется.
1
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
10.04.2013, 16:43  [ТС] 6
вполне работоспособно. Правда, для HomeLisp. Для Common Lisp нужно чуть подправить.
Для меня это сейчас невозможно, т.к. я даже с Common Lisp справиться не могу =)

Исходная функция nums ошибочна:

Lisp
1
2
3
(defun nums(n l x) 
  ((if (check x) (add x l)) ;; лишняя левая скобка
   (if (eq x n) (l) (nums n l (+ x 1)))))
Самая же главная ошибка, на мой взгляд, в том, что вызов (add x l) возвращает список с добавленным x, но исходный l не меняется.
Без скобки "Error: Not inside a block named NIL."
0
Модератор
Эксперт функциональных языков программированияЭксперт Python
36601 / 20330 / 4220
Регистрация: 12.02.2012
Сообщений: 33,643
Записей в блоге: 13
10.04.2013, 17:43 7
Цитата Сообщение от mcmishok Посмотреть сообщение
Без скобки "Error: Not inside a block named NIL."
- конечно, нельзя удалять одну скобку...
0
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
10.04.2013, 17:48  [ТС] 8
Цитата Сообщение от Catstail Посмотреть сообщение
- конечно, нельзя удалять одну скобку...
Ну не одну, закрывающую тоже =)

А вообще спасибо =) уже разобрался. просто недопонимал синтаксис языка.
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
10.04.2013, 18:59 9
Цитата Сообщение от mcmishok Посмотреть сообщение
Преподом предложена среда LispWorks. Ужасная кривая штука.
Что же в ней кривого? Вполне отличная IDE.
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
10.04.2013, 23:28 10

Не по теме:


Цитата Сообщение от korvin_ Посмотреть сообщение
Что же в ней кривого?
Проприетарное, значит, кривое. :P



Добавлено через 34 минуты
mcmishok, вот аналог кода Catstail'а, но 100% pure ANSI Common Lisp:
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
(defun check (x)
  (cond ((eq x 2) t)
        ((> x 1) (loop for i from 2 to (+ (sqrt x) 1)
                       when (zerop (mod x i)) return nil 
                       finally (return t)))))
 
(defun nums (n x)
  (labels ((helper (n l x)
             (cond ((= x n) l)
                   ((check x) (helper n (cons x l) (1+ x)))
                   (t (helper n l (1+ x))))))
    (nreverse (helper n () x))))
По логике ваша функция nums не должна принимать список как аргумент, потому что она составляет список, а не добавляет к списку элементы. Однако для организации рекурсии вам действительно нужна (вспомогательная) функция, которая принимала бы и список. Стандартным решением является использование более общей вспомогательной функции. В ЦЛ они определяются с помощью labels.

Ещё на эту тему: https://www.cyberforum.ru/post4342165.html
1
4699 / 4394 / 380
Регистрация: 12.05.2012
Сообщений: 3,096
11.04.2013, 00:34 11
как вариант:
Lisp
1
2
3
4
5
6
7
8
9
10
11
(defun check (x)
  (when (> x 1) (loop for i from 2 to (1+ (sqrt x))
                      never (zerop (mod x i)))))
 
(defun nums(x n &optional w) 
  (cond ((= x n) (nreverse w))
        ((or (= x 2) (check x)) (nums (1+ x) n (cons x w)))
        ((nums (1+ x) n w))))
 
> (nums 1 30)
(2 3 5 7 11 13 17 19 23 29)
2
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
11.04.2013, 00:38 12
w - тайный аргумент. Если пользователь случайно передаст туда список, то-то удивится, обнаружив его вверх ногами.
0
4699 / 4394 / 380
Регистрация: 12.05.2012
Сообщений: 3,096
11.04.2013, 00:53 13
Lisp
1
2
3
4
5
6
7
8
9
10
(defun nums(x n &optional w) 
  (cond ((= x n) (nreverse w))
        ((or (= x 2) (when (> x 1)
                       (loop for i from 2 to (1+ (sqrt x))
                             never (zerop (mod x i)))))
         (nums (1+ x) n (cons x w)))
        ((nums (1+ x) n w))))
 
> (nums 1 30)
(2 3 5 7 11 13 17 19 23 29)
В решении задач этой случайностью можно пренебречь, как и опасностью использования спискоразрушающих функций, если не использовать присваивания. Скорее он случайно может передать список первым или вторым параметром. Компактный код опциональных параметров имеет свои преимущества перед labels - быстрее править и меньше ошибок.
1
helter
11.04.2013, 02:10
  #14

Не по теме:


Никому не интересный офтоп про точку зрения helter-а.

Цитата Сообщение от _sg Посмотреть сообщение
компактный код опциональных параметров имеет свои преимущества перед labels
Ага.

В Питоне даже декларируется такой подход: всё решается договорённостью. Если в документации написано, что нужно давать функции два параметра, а ты три дал, то вся ответственность ложится на тебя.

Кому как, а меня Коммон Лисп интересует не тем, что в стандарт включили опциональные аргументы функций, а тем, что это язык, который подстраивается под нужды задачи. Грэм пишет: если есть паттерн, имеет смысл создать утилиту.

labels - немного многословнее. Но хуже, наверно, то, что решение получается не в одно действие, а в два: сначала вспомогательная функция, потом само решение.

Я познакомился с Лиспом (и с функциональным программированием) через Scheme. И очень мне нравился там named let. Это как раз синтаксис, передающий рекурсию. Вот я попробовал написать его аналог:
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(defmacro nlet (name bindings &body body)
  (let* ((name name)
         (bindings bindings)
         (vars (mapcar #'first bindings))
         (vals (mapcar #'second bindings)))
    `(labels ((,name (,@vars)
                ,@body))
       (,name ,@vals))))
(defun fac (n)
  (nlet helper ((n n)
                (p 1))
    (if (zerop n)
        p
        (helper (- n 1) (* p n)))))
(defun nums* (n x)
  (nreverse (nlet helper ((l nil)
                          (x x))
              (cond ((= x n) l)
                    ((check x) (helper (cons x l) (1+ x)))
                    (t (helper l (1+ x)))))))
Для примера - факториал и наша функция. Внутри nlet имя (здесь - helper) биндится на вспомогательную функцию.

Задача опять стала в одно действие.

Однако всё равно пришлось ввести в рассмотрение новое имя. Собственно, так и задумано, ведь прототип - именованный let. Их же может быть несколько, да ещё они могут и скакать друг между другом (императивные языки со своими убогими циклами нервно курят в сторонке). Но в нашей-то задаче это вроде ни к чему? Наверно потому, что нам и не нужна мощность рекурсии в полном объёме, а достаточно обойтись одним стандартным средством. do:
Lisp
1
2
3
4
5
6
(defun nums** (n x)
  (do ((l nil (if (check x)
                  (cons x l)
                  l))
       (x x (1+ x)))
    ((= x n) (nreverse l))))
Вот тут, мне кажется, ни прибавить ни убавить, каждая буква по делу.

2
4699 / 4394 / 380
Регистрация: 12.05.2012
Сообщений: 3,096
11.04.2013, 08:40 15
возможно Вы правы - циклы против рекурсивных вызовов:
Lisp
1
2
3
4
(defun nums (n x)
  (do ((w nil (if (check x) (cons x w) w))
       (x x (1+ x)))
      ((= x n) (nreverse w))))
vs
Lisp
1
2
3
4
(defun nums(x n &optional w) 
  (cond ((= x n) (nreverse w))
        ((check x) (nums (1+ x) n (cons x w)))
        ((nums (1+ x) n w))))
но, как по мне - рекурсия краше.
1
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
11.04.2013, 09:19  [ТС] 16
Цитата Сообщение от korvin_ Посмотреть сообщение
Что же в ней кривого? Вполне отличная IDE.
Позволяет удалять текст, выводимый интерпретатором. Периодически вылетает, из-за чего всегда копирую код в блокнот.
0
Модератор
Эксперт функциональных языков программированияЭксперт Python
36601 / 20330 / 4220
Регистрация: 12.02.2012
Сообщений: 33,643
Записей в блоге: 13
11.04.2013, 10:53 17
До кучи (без рекурсии и в одно действие):

Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defun prime-list (n)
  (let ((res nil))
     (iter (for m upfrom 2)
        (when (not
         (iter (for k from 2 to (1+ (flo2fix (sqr m))))
               (when (and (<> m k)(zerop (% m k))) (return t)))) (collecting m into res))
        (when (= (length res) n) (return res)))))
 
==> prime-list
 
(prime-list 10)
 
==> (2 3 5 7 11 13 17 19 23 29)
 
(prime-list 50)
 
==> (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229)
1
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
11.04.2013, 12:04 18
Цитата Сообщение от _sg Посмотреть сообщение
возможно Вы правы - циклы против рекурсивных вызовов:
Да нет. Цикл - частный случай рекурсии. И хвостовая рекурсия - по сути итерация. Оператор do со всеми своими изменениями счётчиков замечательно записывается через ламбду, то есть он вполне функциональный.

Сравните что я написал выше через nlet (рекурсия в собственном соку) и через do. По сути разница только в том, что do - анонимный. Если мы не хотим использовать именованную рекурсивную функцию (потому что задача слишком простая), напрашивается do, потому что он именно для этого и служит: организация сравнительно несложной итерации. И поэтому код с do получился таким кратким и по делу.

Не по теме:


Я бы, наверно, не злоупотреблял опциональными аргументами (оставив их для того, чтобы пользователь мог по своему желанию давать значение или нет), а мог бы сделать синтаксис

Lisp
1
2
3
4
(defundo nums***(x n) (w nil) 
  (cond ((= x n) (nreverse w))
        ((check x) (nums*** (1+ x) n (cons x w)))
        ((nums*** (1+ x) n w))))
что раскрывалось бы в defun с labels. По крайней мере, потренировался бы с макросами. Однако не буду так делать, потому что не вижу, какое здесь преимущество перед do.

0
4699 / 4394 / 380
Регистрация: 12.05.2012
Сообщений: 3,096
11.04.2013, 13:34 19
Lisp
1
2
3
4
5
6
7
8
9
10
(defun check (n)
  (when (> n 1) (loop for a from 2 to (1+ (sqrt n))
                      never (zerop (mod n a)))))
 
(defun nums(n m) 
  (loop for a from n to m
        when (or (= a 2) (check a)) collect a))
 
> (nums 1 30)
(2 3 5 7 11 13 17 19 23 29)
у всех свои предпочтения, студентам важнее ухватить рекурсию, поэтому в задачах по лиспу чаще рекурсия и редко требуют do, dolist, dotimes, loop да и то если преподаватель по привычке думает циклами или они оказываются выразительнее или быстрее рекурсии
0
4 / 4 / 6
Регистрация: 10.04.2013
Сообщений: 45
11.04.2013, 13:35  [ТС] 20
Цитата Сообщение от Catstail Посмотреть сообщение
До кучи (без рекурсии и в одно действие):
я бы с удовольствием делал без рекурсии, но использование рекурсии было оговорено заданием =)

Я смотрю, вы тут увлеклись
0
11.04.2013, 13:35
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
11.04.2013, 13:35
Помогаю со студенческими работами здесь

Новичок
Я в физике полный ноль. Теорию никакую не учил, а из формул знаю только закон Ома и теорию...

C++ C Новичок
Здравствуйте, дорогие друзья! Хочу изучить язык программирования C++ Раньше никогда не...

Новичок
Читаю книгу Зубков С.В. &quot;Assembler для DOS, Windows и UNIX&quot;. Пока мало что понял, но стараюсь....

Новичок в Qt
Здравствуйте! Вот решил открыть для себя Qt. Скачал последнюю версию SDK на оффициальном сайте....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru