如何在Julia中通过id检索对象

con*_*res 5 julia

在朱莉娅,说我有object_id一个变量,但忘记了它的名字,我怎么能用id检索对象?

即我想要倒数some_id = object_id(some_object).

Ste*_*ski 15

正如@DanGetz在评论中所说,object_id是一个哈希函数,并且设计为不可逆.@phg也是正确的,ObjectIdDict正是为了这个目的而准备的(尽管在手册中没有详细讨论,但是有文档记载):

ObjectIdDict([itr])

ObjectIdDict()构造一个哈希表,其中键是(总是)对象标识.不像Dict它的键和值类型没有参数化,因此它的eltype总是如此Pair{Any,Any}.

请参阅Dict进一步的帮助.

换句话说,它通过===使用object_id哈希函数来哈希对象.如果你有一个ObjectIdDict并且你使用你遇到的对象作为其中的键,那么你可以保留它们并稍后通过将它们取出来恢复它们ObjectIdDict.

然而,听起来你想要在没有明确的情况下ObjectIdDict通过询问创建的对象具有给定的内容来执行此操作object_id.如果是这样,请考虑这个思想实验:如果每个对象总是可以从其中恢复object_id,那么系统永远不会丢弃任何对象,因为程序总是可以通过ID请求该对象.所以你永远无法收集任何垃圾,每个程序的内存使用量会迅速扩大,以便使用你所有的RAM和磁盘空间.这相当于ObjectIdDict将每个对象创建的单个全局放入其中.因此,以object_id这种方式反转函数将永远不需要释放任何对象,这意味着您需要无限制的内存.

即使我们拥有无限的记忆,也存在更深层的问题.对象存在意味着什么?在存在优化编译器的情况下,这个问题没有明确的答案.从程序员的角度来看,通常会出现一个对象被创建和操作,但实际上 - 即从硬件的角度来看 - 它永远不会被创建.考虑这个函数,它构造一个复数,然后用它来进行简单的计算:

julia> function f(y::Real)
           z = Complex(0,y)
           w = 2z*im
           return real(w)
       end
f (generic function with 1 method)

julia> foo(123)
-246
Run Code Online (Sandbox Code Playgroud)

从程序员的角度来看,这构造了复数z,然后构造2z,然后2z*im,最后构造real(2z*im)并返回该值.因此,所有这些值都应该插入到"天空中的Great ObjectIdDict"中.但它们真的构建了吗?这是适用于以下功能的此函数的LLVM代码Int:

julia> @code_llvm foo(123)

define i64 @julia_foo_60833(i64) #0 !dbg !5 {
top:
  %1 = shl i64 %0, 1
  %2 = sub i64 0, %1
  ret i64 %2
}
Run Code Online (Sandbox Code Playgroud)

Complex根本没有构建任何值!相反,所有工作都被内联并消除,而不是实际完成.整个计算归结为将参数加倍(通过将其向左移一位)并将其否定(通过从零减去它).这种优化可以首先进行,因为中间步骤没有可观察到的副作用.编译器知道没有办法区分实际构造复杂值和对它们进行操作,只做几个整数操作 - 只要最终结果总是相同的.在"天空中的伟大的ObjectIdDict"的概念中隐含的假设是,所有似乎构造的对象实际上都被构造并插入到一个大的永久数据结构中 - 这是一个巨大的副作用.因此,不仅从其ID中恢复与垃圾收集不兼容的对象,它还与几乎所有可想到的程序优化都不兼容.

可以设想反转的唯一另一种方式是object_id按需计算其逆图像,而不是在创建对象时保存对象.这将解决内存和优化问题.当然,由于存在无限多个可能的对象但只有有限数量的对象ID,所以不可能.在程序中,你实际上不太可能真正遇到两个具有相同ID的对象,但ID空间的有限性意味着原则上不能反转散列函数,因为每个ID值的preimage包含无限数量的潜在对象.

我可能反驳了反object_id函数远远超过必要的可能性,但它导致了一些有趣的思想实验,我希望它有所帮助 - 或者至少是发人深省.实际的答案是,没有办法明确地存储你想要稍后回来的每个对象ObjectIdDict.

  • 在 julia 1.3.1 中,它现在称为“IdDict”,而“object_id”函数现在只是“objectid” (2认同)