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

Зрить в корень проблемы

28.08.2020, 14:23. Показов 712. Ответов 11

Author24 — интернет-сервис помощи студентам
Как-то давно, когда я еще только начинал изучать Лисп, я пробовал писать свои реализации стандартных функций.
Это казалось мне наиболее интересным.
И вот я решил написать функцию, которая будет находить наибольшее число в списке.
Написал: функция хоть и делала то, что надо, но была плохой.
На это мне тогда указал Catstail
Вот она.

Lisp
1
2
3
4
5
6
7
8
(define (bad lst)
  (if (empty? (cdr lst))
      (car lst)
      (if (> (car lst) (bad (cdr lst)))
          (car lst)
          (bad (cdr lst)))))
 
(bad '(1 20 100 3 4 0 500))
Проблема в том, что функция bad тут вычисляется два раза.
Catstail тогда подсказал как надо.

Lisp
1
2
3
4
5
6
7
8
9
10
(define (good lst)
  (if (empty? (cdr lst))
      (car lst)
      (let ((recur (good (cdr lst))))
        (car lst)
        (if (> (car lst) recur)
            (car lst)
            recur))))
 
(good '(1 20 100 3 4 0 500))
Здесь все как надо. Хорошо, когда есть кому подсказать.
Исправлено, работает - вот и славно. Забыли.
Но внутри что-то не давало покоя. И через пару дней я решил загуглить и посмотреть как другие решали эту задачу.
И что я увидел? - Вопрос на StackOwerflow о том, как найти наибольшее число в списке.
И ответ был плохой функцией, которая вычисляет себя два раза. И никто не исправил и не указал на эту ошибку.
Человек так и пошел дальше с ошибочным представлением об этом алгоритме. А что, работает ведь?
Тут задумался - " А почему мы делаем именно так. Что разных людей заставляет приходить к одному и тому же ошибочному решению?"
Вот тогда я посмотрел впервые на проблему реально. Вместо того, чтобы пытаться поскорее бездумно решить проблему нужно посмотреть на ее причину. А причина проста - функция возвращает
Lisp
1
>
булево значение. Именно это заставляет писать неуклюжий ошибочный код: вместо того, чтобы обойти гору, человек начинает на нее лезть.
Но ларчик же просто открывается, если призадуматься на секунду.
Lisp
1
2
3
4
5
6
(define (grt lst)
  (if (null? (cdr lst))
      (car lst)
      (let ((greater (λ (x y)
                       (if (> x y) x y))))
        (greater (car lst) (grt (cdr lst))))))
Вместо велосипеда простой и понятный код, который решает поставленную задачу.
В тот момент я понял как и почему пишется так называемый быдло код и почему потом происходят разные проблемы с безопасностью
и утечками памяти.
Я думаю вот почему так важно изучать такие языки, как Lisp. Они наглядно дают это понять.
2
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
28.08.2020, 14:23
Ответы с готовыми решениями:

не могу найти корень проблемы
При запуске игры, вылетает такая ошибка (см. скриншот). До этого таких проблем не возникало....

Не могу понять корень проблемы. Ноутбук DELL Inspiron 7577
Проблема с ноутбуком Ноутбук DELL Inspiron 7577. проц - i7-7700, видяха - 1060, оперативы 16gb. ...

Найти корень уравнения методом последовательных итераций.Второй корень вычисляет неверно
Задание:Написать программу для вычисления методом последовательных итераций уравнения x=Aexp(-x)....

Вычислить массу пластинки ограниченной линиями: y=корень(x-1) , y=0, x=2 с поверхностной плотностью пропорциональной 2*корень(x) .
Вычислить массу пластинки ограниченной линиями: y=корень(x-1) , y=0, x=2 с поверхностной...

11
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
28.08.2020, 15:08 2
Цитата Сообщение от sodda Посмотреть сообщение
булево значение
Причина - булева слепота!

Кстати, схема из-за #t и #f более ей подвержена, чем CL.

Цитата Сообщение от sodda Посмотреть сообщение
Вместо велосипеда простой и понятный код, который решает поставленную задачу.
В принципе, это тоже велосипед - расписанная свёртка. Так-то это (reduce #'max list).
0
Заблокирован
28.08.2020, 15:26  [ТС] 3
Цитата Сообщение от helter Посмотреть сообщение
В принципе, это тоже велосипед - расписанная свёртка. Так-то это (reduce #'max list).
Нужно было написать функцию именно самому.

Цитата Сообщение от helter Посмотреть сообщение
Кстати, схема из-за #t и #f более ей подвержена, чем CL.
В CL тоже есть T и F
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
28.08.2020, 15:51 4
Цитата Сообщение от sodda Посмотреть сообщение
В CL тоже есть T и F
Точнее, nil, а не f, но дело в том, что в CL булево значение ассоциировано с любым объектом, а в схеме - только с #t и #f. В CL истинное значение несёт сколько угодно дополнительной информации.

Булева слепота возникает из-за того, что собственно булево значение несёт мало информации - один бит. Поэтому до ветвления часто вычислено больше информации, чем нужно для принятия решения, и её надо протаскивать в ветки. По-моему, функции bad и good идеально это иллюстрируют. А функция grt - что неплохо и вовсе обойтись без ветвления.
0
Заблокирован
28.08.2020, 16:23  [ТС] 5
Цитата Сообщение от helter Посмотреть сообщение
Точнее, nil, а не f, но дело в том, что в CL булево значение ассоциировано с любым объектом, а в схеме - только с #t и #f. В CL истинное значение несёт сколько угодно дополнительной информации.
Это да. Там вместо nil есть void, но это немного другое.
Nil, кстати, тоже может быть опасен в неумелых руках.
например у нас есть вектор (Пишу на Clojure, так как там есть nil)

Lisp
1
(def v [1, 2, 3, nil])
nil - это тоже просто значение.

Может возникнуть такая ситуация

Lisp
1
2
3
(get v 3)
 
=> nil
nil - это отсутствие значения или значение в векторе?

Поэтому функции для доступа к элементам коллекций принимает второй необязательный аргумент.

Lisp
1
2
3
(get v 4 "Nothing")
 
=> "Nothing"
Теперь точно ясно, что значения с индексом 4 нет в векторе.

А что в CL функция
Lisp
1
>
может вернуть не булево значение?
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
28.08.2020, 17:06 6
Цитата Сообщение от sodda Посмотреть сообщение
может вернуть не булево значение?
Вообще, стандарт пишет "generalized boolean", то есть теоретически может быть что угодно. Но сомневаюсь, что какая-нибудь реализация возвращает что-либо кроме t или nil.

Это я просто лишний раз прорекламировал дизайн языка, но для данных примеров это неважно. На CL эти решения выглядели бы аналогично, но их недостатком была бы рекурсия вместо цикла или reduce.

Цитата Сообщение от sodda Посмотреть сообщение
nil - это отсутствие значения или значение в векторе?
Ага, в CL известный пример - find со своими родственниками. У Грэма, что ли, было написано, что из-за этого лиспер может проронить скупую слезу. Если есть опасность найти nil, можно пользоваться другими функциями (member, position). А gethash, чтобы избежать аналогичной проблемы, вторым значением возвращает флаг "найдено?".


Цитата Сообщение от sodda Посмотреть сообщение
Теперь точно ясно, что значения с индексом 4 нет в векторе.
Неа. Там может быть строка "Nothing". Будет надёжно, если использовать объект, сгенерированный на ходу: генсим или свежую конс-ячейку.

Добавлено через 7 минут

Не по теме:

Немного пишу на котлине - туда насовали средств контроля над null-ами, и стала вырастать альтернативная условная система: не-null/null параллельно с true/false. Конечно, из-за джавы они от true и false не откажутся. Но вообще, забавно.

0
Заблокирован
28.08.2020, 18:58  [ТС] 7
Цитата Сообщение от helter Посмотреть сообщение
Ага, в CL известный пример - find со своими родственниками.
В Clojure тоже есть find.

Цитата Сообщение от helter Посмотреть сообщение
Там может быть строка "Nothing"
Возвращать можно что угодно. "Nothing" - как пример был.

Цитата Сообщение от helter Посмотреть сообщение
Не по теме:
Немного пишу на котлине - туда насовали средств контроля над null-ами, и стала вырастать альтернативная условная система: не-null/null параллельно с true/false. Конечно, из-за джавы они от true и false не откажутся. Но вообще, забавно.
Null - это та вещь от которой нужно избавиться раз и навсегда.
Сам изобретатель этого null потом статью написал - "Null - ошибка ценой в миллион долларов"
В Rust, например, отказались от null поэтому и вместо этого используют перечисления Result, которое может иметь значение или возвращает ошибку. И перечисление Option, которое может иметь значение или возвращает None. Суть в том, что эти перечисления в любом случае нужно проверить на отсутствие ошибки или отсутствие результата: получить доступ к значению, если оно есть, иначе невозможно.
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
28.08.2020, 22:00 8
Цитата Сообщение от sodda Посмотреть сообщение
Возвращать можно что угодно. "Nothing" - как пример был.
Я понимаю. "Nothing" точно так же не работает для всех списков, как не работает для них nil. И никакая другая строка. Я предполагаю, не зная Clojure, что универсальное решение для любых списков должно включать создание на лету уникального объекта.

Цитата Сообщение от sodda Посмотреть сообщение
Null - это та вещь от которой нужно избавиться раз и навсегда.
Не слышал, чтобы в лиспе от nil были проблемы. И в строго типизированных языках типы Option весьма пригождаются. От этого добра больше прока, чем от двузначного булева типа.

Цитата Сообщение от sodda Посмотреть сообщение
None
Есть какая-то философская разница между None и null? Не знаю раста.

Цитата Сообщение от sodda Посмотреть сообщение
Суть в том, что эти перечисления в любом случае нужно проверить на отсутствие ошибки или отсутствие результата: получить доступ к значению, если оно есть, иначе невозможно.
Конечно. Они же в качестве логических значений, поэтому естественно участвуют в ветвлениях. Которые необязательно if-ами делать. Вот, в котлине сделали монаду Option: ?..
0
Заблокирован
28.08.2020, 22:43  [ТС] 9
Цитата Сообщение от helter Посмотреть сообщение
Я понимаю. "Nothing" точно так же не работает для всех списков, как не работает для них nil. И никакая другая строка. Я предполагаю, не зная Clojure, что универсальное решение для любых списков должно включать создание на лету уникального объекта.
Безусловно. Но вероятность что так окажется "Nothing" куда ниже, чем то, что там будет nil.

Цитата Сообщение от helter Посмотреть сообщение
Не слышал, чтобы в лиспе от nil были проблемы. И в строго типизированных языках типы Option весьма пригождаются. От этого добра больше прока, чем от двузначного булева типа.
Ну возможные проблемы мы уже затронули выше, когда не понятно значение это или нет. Lisp - это бестиповый язык. Тем более, что современные диалекты стремятся все к большей функциональности. Например в том же С++ с null возможны серьезные проблемы.

Цитата Сообщение от helter Посмотреть сообщение
Есть какая-то философская разница между None и null? Не знаю раста.
Безусловно. В самих словах, конечно, нет - None или Null - нет разницы.
Нов подходе она очевидна. None - это перечисление типа Option, а получить доступ к перечислениям не так-то просто, как я говорил. Для этого нужно использовать match или другие конструкции языка.

Например:

Lisp
1
2
3
4
5
6
let x = None;
 
match x {
   Some(y) => println!("{}", y),
   None    => panic!("The value is None")
}
Не обработать результат None невозможно.
Если написать match без ветви None, то компилятор заругается.

Цитата Сообщение от helter Посмотреть сообщение
Вот, в котлине сделали монаду Option: ?.
Скорее всего из Rust пришла. В Rust это сахар для макроса try!
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
30.08.2020, 10:01 10
Цитата Сообщение от helter Посмотреть сообщение
Точнее, nil, а не f, но дело в том, что в CL булево значение ассоциировано с любым объектом, а в схеме - только с #t и #f.
Это никак особо не мешает, вместо #t можно вернуть любое значение.
0
4527 / 3521 / 358
Регистрация: 12.03.2013
Сообщений: 6,038
30.08.2020, 12:51 11
Цитата Сообщение от korvin_ Посмотреть сообщение
Это никак особо не мешает, вместо #t можно вернуть любое значение.
Во дела, мне почему-то казалось, что в качестве логических должны быть именно #f и #f. Завязываю про схему писать.
0
Эксперт функциональных языков программированияЭксперт Java
4486 / 2721 / 485
Регистрация: 28.04.2012
Сообщений: 8,590
30.08.2020, 13:11 12
Цитата Сообщение от helter Посмотреть сообщение
Во дела, мне почему-то казалось, что в качестве логических должны быть именно #t и #f
Отличие от CL только в том, что nil не является #f.

Добавлено через 5 минут
https://ideone.com/aQ19bT
0
30.08.2020, 13:11
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.08.2020, 13:11
Помогаю со студенческими работами здесь

С помощью рекурсивной функции найдити квадратный корень Y=корень из X, воспользовавшись итерационной формулой Ньютона
С помощью рекурсивной функции найдите с заданной точностью квадратный корень Y=корень из X ,...

Напишите программу «КОРЕНЬ», которая запрашивает число и выдает корень квадратный из заданного числа
Напишите программу «КОРЕНЬ», которая запрашивает число и выдает корень квадратный из заданного...

Найти корень нелинейного уравнения F(x)=0 методом половинного деления. Крайние значения предела ([a,b]) , содержащий корень и погрешность (\epsilon )
Найти корень нелинейного уравнения F(x)=0 методом половинного деления. Крайние значения предела ()...

Найти корень нелинейного уравнения F(x)=0 методом хорд. Крайние значения предела ([a,b]) содержащий корень и погрешность (\epsilon ) вводятся с клавиа
Найти корень нелинейного уравнения F(x)=0 методом хорд. Крайние значения предела () содержащий...

Найти корень нелинейного уравнения F(x)=0 методом касательных (метод Ньютона). Крайние значения предела ([a,b]) содержащий корень и погрешность (\epsi
Найти корень нелинейного уравнения F(x)=0 методом касательных (метод Ньютона). Крайние значения...

Проблемы с инетом, не отправляется почта, проблемы со связью с другой организацией
Здравствуйте. Есть проблемка. Такая ситуация - Организация. Компьютеры подключены по локалке....


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

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