Есть ли способ получить уникальный идентификатор объекта в Racket? Например, когда мы используем оператор Racket eq?
, чтобы проверить, относятся ли две переменные к объекту такой же, какой идентификатор он использует для достижения этого сравнения?
Я ищу что-то вроде id
функция в Python или object_id
метод в Ruby, другими словами, какую-то функцию id
такую, что (= (id obj) (id obj2))
означает, что (eq? obj obj2)
истинно.
Некоторые соответствующие документы:
К сожалению, я сейчас точно не помню, но это было как-то связано с пониманием того, как работает интерпретатор Racket в каком-то контексте, или, может быть, это было связано с «готовыми» типами структур. В любом случае, я считаю, что это было связано с обучением. Если я вспомню, что это было, или придумаю другой достойный вариант использования, я планирую добавить здесь комментарий.
Тождества вы, скорее всего, не найдете, но сам объект только eq?
сам с собой и ничем другим. eq?
в основном сравнивает адресное расположение значений. Поэтому, если вам нужен идентификатор, вы можете просто сохранить весь объект в этом месте, и он будет уникальным.
Место — это привязка. Думайте об этом как об адресе, который вы не можете получить, и адресе, который имеет адрес объекта. Например. привязка ((lambda (a) a) 10)
будет хранить адрес расположения объекта 10
в первом адресе стека, а код в теле просто возвращает тот же адрес. Местоположение может измениться на set!
, но вы никогда не получите его местоположение в памяти.
В системах lisp принято хранить значения в указателях. Это означает, что некоторые типы и значения на самом деле не имеют объекта по адресу, но в адресе есть значение и тип, закодированные в нем, которые известны системе. Как правило, небольшие целые числа, символы, символы и логические значения могут быть равны указателю, даже если они созданы в разное время. например. '(1 2 3)
будет использовать только 3 пары и не будет пробела для значений 1-3 и ()
.
Есть способ получить C-указатель объекта через ffi/unsafe
, с очевидной оговоркой, что это НЕБЕЗОПАСНО.
;; from https://rosettacode.org/wiki/Address_of_a_variable#Racket
(require ffi/unsafe)
(define (madness v) ; i'm so sorry
(cast v _racket _gcpointer))
Чтобы использовать его:
(define a (list 1 2))
(define b (list 1 2))
(printf "a and b have different address: ~a ~a\n"
(equal? (madness a) (madness b))
(eq? a b))
(printf "a and a have the same address: ~a ~a\n"
(equal? (madness a) (madness a))
(eq? a a))
(printf "1 and 1 have the same address: ~a ~a\n"
(equal? (madness 1) (madness 1))
(eq? 1 1))
Хотя указатель не является числом или идентификатором. Это непрозрачный объект... Так что в каком-то смысле это бесполезно. Вместо этого вы могли бы использовать настоящие объекты с eq?
.
Я также не знаю никаких гарантий этого метода. В частности, я не знаю, будет ли указатель обновлен до своего последнего значения, когда копия GC копирует объекты.
Вот реализация такой функции с использованием слабой хеш-таблицы. Использование слабой хэш-таблицы гарантирует правильную сборку мусора для объектов. даже если мы дали ему идентификатор.
#lang racket
(define ht (make-weak-hasheq))
(define next 0)
(define (get-id x)
(define id (hash-ref ht x #f))
(or id
(begin0
next
(hash-set! ht x next)
(set! next (+ next 1)))))
(get-id 'a)
(get-id 'b)
(get-id 'a)
Обратите внимание, что совет Сильвестра здравый. Стандарт заключается в непосредственном сохранении значения.
Для чего вам нужен такой номер?