LINUX.ORG.RU

Добавьте пример letrec

 


0

2

r5rs приводит пример

(letrec ((even?
          (lambda (n)
            (if (zero? n)
                #t
                (odd? (- n 1))))) ;; ---> <bindings> should have 
                         ;;the form ((<variable1> <init1>)
                         ;;в данном случае
                         ;;<variable1> n
                         ;;<init1> (lambda (n) ...)
         (odd?
          (lambda (n)
            (if (zero? n)
                #f
                (even? (- n 1)))))) ;;---> <bindings>
  (even? 88)) ;;---> <body>

library syntax: (letrec <bindings> <body>)

Из-за того что

library procedure: (odd? n)

library procedure: (even? n)

These numerical predicates test a number for a particular property, returning #t or #f.

пример выглядит не совсем понятным.

Бросьте более читаемый примерчик использования letrec.

;;в данном случае
;;<variable1> n
;;<init1> (lambda (n) ...)

В данном случае <variable1> even?. Ведь начало (even? (lambda …

library procedure: (odd? n)

Если не компилируется из-за того, что уже определена, можно написать так:

(letrec ((my-even?
          (lambda (n)
            (if (zero? n)
                #t
                (my-odd? (- n 1)))))
         (my-odd?
          (lambda (n)
            (if (zero? n)
                #f
                (my-even? (- n 1))))))
  (even? 88))
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

В данном случае even?. Ведь начало (even? (lambda …

тем более, у меня в мозгах не укладывается.

Можешь сбросить такой пример с letrec который дураку понятен был, хотя дурак здесь не совсем уместен.

Вот такое почитал, Fixing Letrec: A Faithful Yet Efficient Implementation of Scheme’s Recursive Binding Construct круто. Но не всё понятно для меня как для user.

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

Вот ещё, в программировании есть какая-то фишка тривиальности, этот пример похоже и является этой фишкой, а я чего-то не догоняю.

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

Это простой пример рекурсивной имплементации функций even? и odd?. Блок letrec определяет две взаимно-рекурсивные функции, even? и odd?, каждая из которых вызывает другую с аргументом, уменьшенным на единицу (так как если n четное, то n - 1 – нечетное, и наоборот). Так путем какого-то количества поочередных вычитаний и вызовов проблема сводится либо к (even? 0), либо к (odd? 0), что является уже известным фактом.

Блок letrec здесь нужен для эффективного и выразительного объявления нескольких выражений, ссылающихся друг на друга. Собственно, цитата с racket docs

Like let, including left-to-right evaluation of the val-exprs, but the locations for all ids are created first, all ids are bound in all val-exprs as well as the bodys, and each id is initialized immediately after the corresponding val-expr is evaluated. The ids must be distinct according to bound-identifier=?.

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

Блок letrec здесь нужен для эффективного и выразительного объявления нескольких выражений, ссылающихся друг на друга.

Здорово. Я где-то рядом с пониманием фишки тривиальности программирования. Сможете удержать мысль.

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

Можешь сбросить такой пример с letrec который дураку понятен был, хотя дурак здесь не совсем уместен.

Обычный let понятен?

(let ((x 1)
      (y 2))
  (+ x y))

внутри тела x = 1, y = 2.

В scheme функции такие же значения. Можно писать

(let ((x 1)
      (f (lambda (n) (+ n 1))))
  (f x))

В выражении (f x) x = 1, f = (lambda (n) (+ n 1))

Функции могут ссылаться на предыдущие

(let ((f (lambda (n) (+ n 1))
      (g (lambda (n) (f (- n 1)))))
  (g 5))

но не могут ссылаться на определённые ниже

(let ((f (lambda (n) (if (zero? n) #f (g (- n 1)))))
      (g (lambda (n) (if (zero? n) #t (f (- n 1))))))
  (g 5))

выдаст ошибку, что в первой строке есть ссылка на неопределённую функцию g.

Если же заменить let на letrec, то все определяемые имена доступны во всех определениях letrec.

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

В scheme функции такие же значения. Можно писать

(let ((x 1)
      (f (lambda (n) (+ n 1))))
  (f x))

В выражении (f x) x = 1, f = (lambda (n) (+ n 1))

проходим let -> (f x) -> n=>1 -> (f x) -> 2

пытаюсь додуматься

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

Какой-то вопрос остался?

В общем-то да, судя по тому как ты пишешь код, ты явно используешь фишку тривиальности программирования, а я не могу поймать эту фишку.

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

Суть в том, что программирование — это просто перевод. Надо написать фразу по-русски, а потом подстрочником переводить её на выбранный язык программирования.

Или наоборот, чтобы понять программу, просто перевести её на русский.

(letrec ((f (lambda (n) (if (zero? n) #f (g (- n 1)))))
         (g (lambda (n) (if (zero? n) #t (f (- n 1))))))
  (g 5))

Определим, используя определяемые символы в других определениях:
 f - это функция от n: если n равно нулю, возвращает ложь, иначе вызывает g от n-1
 g - это функция от n: если n равно нулю, возвращает истину, иначе вызывает f от n-1
Вызовем, с учётом описанных определений g от 5.

P.S. Было бы легче понять программу, если бы она выглядела так:

рекурсивно-определяя
  ;
    f $ функция (n) 
      ecли (ноль? n) ложь g(n - 1)
    g $ функция (n)
      ecли (ноль? n) истина f(n - 1)
  g 5

?

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

Или наоборот, чтобы понять программу, просто перевести её на русский.

Класс.

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

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

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

Математическая часть нужна только чтобы спроектировать достаточно оптимальный алгоритм.

Если алгоритм можно где-то прочитать, математическая часть необязательна. Нужно только понимать язык, на котором описан алгоритм достаточно, чтобы понять алгоритм. И знать язык программирования, на котором пишешь, достаточно, чтобы этот алгоритм на него перевести.

Тривиально пишу код в тех областях, где уже прочитал мегабайты чужого кода.

Не обижайся я пытаюсь поставить точку над самообучением

У меня самообучение сводилось к чтению чужого кода (спасибо OpenSource, его МНОГО). И иногда к чтению документации по языку программирования, если я вдруг понимал, что не понимаю, что обозначает какая-либо строка из прочитанного.

Затем к написанию программ для решения своих задач.

Ещё можно обучаться на задачах из https://www.codingame.com/start: если получилось решить задачу хоть как-нибудь, показывают решения других участников. Среди них часто попадаются очень красивые.

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

Функции могут ссылаться на предыдущие

Тогда уж let*, а не let. Простой let создаст привязки в контексте, где ни f, ни g ещё не определены, так что g не может сослаться на f, равно как и g на саму себя.

Впрочем, даже если привязки уже есть (в окружении верхнего уровня), это не сработает, т. к. функции f и g все равно останутся в предыдущем окружении, лишь тело let будет в новом окружении. Так что тут нужен letrec.

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

Тогда уж let*, а не let. Простой let создаст привязки в контексте, где ни f, ни g ещё не определены, так что g не может сослаться на f, равно как и g на саму себя.

Прошу меня простить. Действительно перепутал let и let*.

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

У меня самообучение сводилось к чтению чужого кода (спасибо OpenSource, его >МНОГО).

Когда-то я это проходил, например берём код из

https://github.com/racket/draw

открываем, какую папку, с какого файла начинать читать или просто читать разрозненные определения функций? На этом я и сдулся когда-то. Как поступал ты?

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

Сначала читал. Правда начинать лучше не с draw и gui, а с чего-то типа https://github.com/racket/racket/blob/master/racket/collects/racket/list.rkt и прочего из https://github.com/racket/racket/blob/master/racket/collects/racket/

Пакеты draw и gui следует читать, когда уже всё понятно кроме ООП.

Потом прочитал http://htdp.org/2020-8-1/Book/index.html , где написано, как написать 90% функции вообще не включая головной мозг, опираясь только на обрабатываемые типы.

Переписал пару примеров из http://gigamonkeys.com/book/ : получилось https://github.com/Kalimehtar/binary-class , https://github.com/Kalimehtar/binary-class-exif , https://github.com/Kalimehtar/binary-class-mp3 , https://github.com/Kalimehtar/binary-class-dbf .

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

поковырялся я там, так и не сообразил как там чего.

Да, плохой пример. Там лиспов нет. Я там по Haskell тренировался.

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

Потом прочитал http://htdp.org/2020-8-1/Book/index.html , где написано, как >написать 90% функции вообще не включая головной мозг, опираясь только на >обрабатываемые типы.

Странно я такого там не встречал.

Переписал пару примеров из http://gigamonkeys.com/book/ : получилось >https://github.com/Kalimehtar/binary-class , https://github.com/Kalimehtar>/binary-class-exif , https://github.com/Kalimehtar/binary-class-mp3 , >https://github.com/Kalimehtar/binary-class-dbf .

Я бы не додумался.

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

https://github.com/racket/racket/blob/master/racket/collects/racket/list.rkt

Загрузил и

(require (for-syntax racket/base) racket/private/list-predicates)

;; unsaved editor:64:9: cannot open module file module path: racket/private/list-predicates path: /usr…acket/collects/racket/private/list-predicates.rkt system error: No such file or directory; errno=2 #(1153 30)

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

Там читать надо и понимать, а не грузить.

А грузить можно из актуальной версии. Открываешь в DrRacket пустой файл, пишешь

#lang racket
takef

Правой кнопкой на takef и «Open defining file». Открывается list.rkt, в котором исходник takef. Я так находил функции, по которым документация не совсем очевидная. Типа register-finalizer.

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

Правой кнопкой на takef и «Open defining file». Открывается list.rkt, в >котором исходник takef. Я так находил функции, по которым документация не >совсем очевидная. Типа register-finalizer.

Вот это надо.

Там читать надо и понимать, а не грузить.

Начал припоминать что к чему.

register-finalizer

Такой функции нет. Достаточно questionable file, но не спорю.

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

Странно я такого там не встречал.

http://htdp.org/2020-8-1/Book/part_one.html#%28part._sec~3adesign-func%29

и

http://htdp.org/2020-8-1/Book/part_two.html#%28part._sec~3alists-programming%29

По типу данных пишешь всю структуру функции, а только потом начинаешь думать, какой результат в какой ветке нужен.

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

Переписал пару примеров из http://gigamonkeys.com/book/ : получилось >https://github.com/Kalimehtar/binary-class , https://github.com/Kalimehtar>/binary-class-exif , https://github.com/Kalimehtar/binary-class-mp3 , >https://github.com/Kalimehtar/binary-class-dbf .

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

Мои задачки ты видел, как тут быть?

http://gigamonkeys.com/book/

там Lisp, зачем переписывать на scheme?

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

там Lisp, зачем переписывать на scheme?

Там использование механизмов Common Lisp, которых нет (готовых) в Racket. Убеждался, что я достаточно понимаю оба языка, чтобы решить эти задачи так, чтобы решение было не хуже лиспового.

Мои задачки ты видел, как тут быть?

Просто решать. В твоих задачках 90% — это получение общей математической формулы для ответа. Или физико-математическое описание решения.

Чем больше решаешь, тем дальше проще. Как с пониманием иностранного языка.

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

Там использование механизмов Common Lisp, которых нет (готовых) в Racket.

это вот такое

(defun sumL1 (a b)
  (+ a b))

(defun sumL2
    (lambda (a b) ;;такое не пролазеет
    (+ a b))) 

#lang racket

(define sum1
  (lambda (a b)
    (+ a b)))

(define (sum2 a b)
    (+ a b))
saufesma
() автор топика
Ответ на: комментарий от saufesma

define в Scheme — это примерно setf в Common lisp.

(setf sumL2
    (lambda (a b)
    (+ a b))) 

работает.

Там использование механизмов Common Lisp, которых нет (готовых) в Racket.

Это я про defmethod и restart-bind

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

Чем больше решаешь, тем дальше проще. Как с пониманием иностранного языка.

Знаешь, scheme не так идёт как lisp, lisp как-то проще, если конечно не лезть в ваши программистские дела. Буду грызть, вариантов таки нет.

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

И иногда к чтению документации по языку программирования, если я вдруг >понимал, что не понимаю, что обозначает какая-либо строка из прочитанного.

У меня не так, был бы признателен если бы ты показал вот это

https://docs.racket-lang.org/guide/classes.html

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

И ещё статейка основана на

https://www.cs.utah.edu/plt/publications/aplas06-fff.pdf

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

У меня не так, был бы признателен если бы ты показал вот это

Для этого надо понять, что непонятно.

Если общие понятия на пальцах, то (class …) описывает некий шаблон данных, который называется класс. В шаблоне указано, что выполняется при создании объекта класса, какие поля есть у объекта данных и какие функции для работы с этими полями доступны. Класс может быть наследником другого класса: тогда сначала будет выполнен класс-родитель (с созданием полей и функций), затем можно добавить новые действия, поля и функции.

В примере про рыбу: «есть класс рыб, у каждого объекта этого класса есть размер, к объекту можно применить get-size (получить размер), grow (увеличить размер), eat (увеличить размер на размер другого объекта класса рыб)».

Чтобы имея класс получить объект используется функция new.

mixin — это функция, аргумент которой класс и результат которой класс.

trait — это набор миксинов. Функцией trait->mixin из него создаётся миксин.

А вообще, в реальных программах такое редко требуется. Как правило, достаточно функций и структур.

monk ★★★★★
()
Ответ на: комментарий от monk
#lang racket
(class object%
  (init size)                ; initialization argument
 
  (define current-size size) ; field
 
  (super-new)                ; superclass initialization
 
  (define/public (get-size)
    current-size)
 
  (define/public (grow amt)
    (set! current-size (+ amt current-size)))
 
  (define/public (eat other-fish)
    (grow (send other-fish get-size))))

(define fish% (class object% (init size))) ;класс рыбка
#<class:unsaved-editor:2:0>   все норм
если прогнать код до этой строки

(define charlie (new fish% [size 10])) ;весь код с этой строкой выдал
instantiate: superclass initialization not invoked by initialization
  class name: fish%

Где искать эту ошибку?

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

подправил и

#lang racket
(class object%
  (init size) (super-new))               ; initialization argument
 
  (define current-size size) ; field
 
  (define/public (get-size) ;unsaved editor:7:2: define/public: use of a ;class keyword is not in a class top-level in: (define/public (get-size) ;current-size)  
;  #(139 43)
    current-size)
 
  (define/public (grow amt)
    (set! current-size (+ amt current-size)))
 
  (define/public (eat other-fish)
    (grow (send other-fish get-size)))

    (define fish% (class object% (init size)))

(define charlie (new fish% [size 10]))

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

Внутри класса обязателен вызов (super-new).

там был этот вызов

(class object%
  (init size)                ; initialization argument
 
  (define current-size size) ; field
 
  (super-new)                ; superclass initialization
 .....)
saufesma
() автор топика
Ответ на: комментарий от monk

Ага, вот

[code] (define fish% (class object% (init size) (super-new))) [/code]

saufesma
() автор топика
Ответ на: комментарий от monk
#lang racket
(class object%
  (init size) (super-new)               ; initialization argument
 
  (define current-size size) ; field
 
  (define/public (get-size)
    current-size)
 
  (define/public (grow amt)
    (set! current-size (+ amt current-size)))
 
  (define/public (eat other-fish)
    (grow (send other-fish get-size))))

    

    (define fish% (class object% (init size) (super-new)))

(define charlie (new fish% [size 10]))
(send charlie grow 6);; send: no such method
                     ;; method name: grow
                     ;; class name: fish%
(send charlie get-size)

метод есть

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

метод есть

Нету.

У тебя charlie определяется как

(define charlie (new fish% [size 10]))

А класс fish% определяется как

(define fish% (class object% (init size) (super-new)))

Тут нет никаких методов. Есть только параметр size, с которым ничего не делается.

grow есть у безымянного класса выше. Но так как значение того класса ничему не присвоено, то создать экземпляр того класса нельзя.

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

там был этот вызов

Это другой класс. И он не сохранён ни в какую переменную. Безымянный.

Считай, что ты написал

(+ 2 2)

(define fish% 0)

и удивляешься, почему в fish% не попало 4 из результата (+ 2 2).

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

безымянного класса выше.

почему они так написали, ведь можно же было и так

#lang racket
(define fish%
(class object%
  (init size)              ; initialization argument
 
  (define current-size size) ; field

  (super-new) 
  
  (define/public (get-size)
    current-size)
 
  (define/public (grow amt)
    (set! current-size (+ amt current-size)))
 
  (define/public (eat other-fish)
    (grow (send other-fish get-size)))))

(define charlie (new fish% [size 10]))
(send charlie grow 6)
(send charlie get-size)

если бы ты не указал

Тут нет никаких методов.

я бы не разобрался.

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

почему они так написали, ведь можно же было и так

Они так и написали. Сначала дали выражение для описания класса.

А потом указали, что можно хранить класс в переменной:

Of course, we can also name the class and its instance:

(define fish% (class object% (init size) ….))

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

Читал, а что читал?

Кусочек кода попробовал не пошло психанул, плюнул, сдулся.

saufesma
() автор топика
Последнее исправление: saufesma (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.