有没有办法获得A a a feference'到可变的结构场
因此,我有一个具有可变字段的记录类型:
type mpoint = { mutable x:int ; mutable y: int };;
let apoint = { x=3 ; y=4};;
而且我的功能期望“ ref”并对其内容做点事。 例如:
let increment x = x := !x+1;;
val increment : int ref -> unit = <fun>
是否有一种方法可以从可变的字段中获取“参考”,以便我可以将其传递给函数。即我想做类似的事情:
increment apoint.x;; (* increment value of the x field 'in place' *)
Error: This expression has type int but an expression was expected of type
int ref
但是上面的事情不起作用,因为apoint.x
返回字段的值,而不是其“ ref”。如果这是Golang或C ++,也许我们可以使用&amp;
操作员指示我们需要地址而不是字段的值:&amp; apoint.x
。
(我们如何)在OCAML中执行此操作?
PS:是的,我知道以这种方式避免使用副作用可能更常见。但是我保证,我这样做是有充分理由的,在这种情况下,这比这个简化/人为的例子更有意义。
So I have a record type with mutable field:
type mpoint = { mutable x:int ; mutable y: int };;
let apoint = { x=3 ; y=4};;
And I have a function that expects a 'ref' and does something to its contents.
For example:
let increment x = x := !x+1;;
val increment : int ref -> unit = <fun>
Is there a way to get a 'reference' from a mutable field so that I can pass it to the function. I.e. I want to do something like:
increment apoint.x;; (* increment value of the x field 'in place' *)
Error: This expression has type int but an expression was expected of type
int ref
But the above doesn't work because apoint.x
returns the value of the field not its 'ref'. If this was golang or C++ maybe we could use the &
operator to indicate we want the address instead of the value of the field: &apoint.x
.
(How) can we do this in Ocaml?
PS: Yes, I know its probably more common to avoid using side-effects in this way. But I promise, I am doing this for a good reason in a context where it makes more sense than this simplified/contrived example might suggest.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
无法准确地做您要求的事情。参考的类型非常具体:
参考是一个可突出字段的记录,名为
contents
。您不能真正从其他一些记录的任意可变领域来构建它。即使您愿意对类型系统撒谎,记录的字段也与记录完全没有表示。您可以将您的字段声明为实际引用:
然后没有问题,
apoint.x
确实是参考。但是,这种表示并不那么高效,即,它需要更多的内存,并且有更多的删除来访问值。如果以命令式设计API,则很难在OCAML中使用。无论如何,这就是我的看法。另一种说法是INT很小。接口也许应该接受int并返回新的int,而不是接受对int的引用并将其修改为适当的。
There's no way to do exactly what you ask for. The type of a reference is very specific:
A reference is a record with one mutable field named
contents
. You can't really fabricate this up from an arbitrary mutable field of some other record. Even if you are willing to lie to the type system, a field of a record is not represented at all the same as a record.You could declare your fields as actual references:
Then there is no problem,
apoint.x
really is a reference. But this representation is not as efficient, i.e., it takes more memory and there are more dereferences to access the values.If an API is designed in an imperative style it will be difficult to use in OCaml. That's how I look at it anyway. Another way to say this is that ints are small. The interface should perhaps accept an int and return a new int, rather than accepting a reference to an int and modifying it in place.
杰弗里·斯科菲尔德(Jeffrey Scofield)解释了为什么从类型系统的角度开始在OCAML中做到这一点。
但是您也可以从GC(垃圾收集器)的角度看它。在OCAML中,所有内容都是琐碎的类型(int,bool,char,...),其存储为31/63位值,或者是指向内存块的指针。每个内存块都有一个描述GC内容的标头,并且具有GC使用的一些额外位。
当您在内部查看参考时,它是一个指向内存块的指针,其中包含
Mutable内容
的记录。通过该尖端,GC可以访问标头,并且知道存储器的块仍然可以到达。但是,让我们假设您可以将
apoint.y
传递给参考的函数。然后,指针将指向apoint
的中间,并且GC试图访问该块的标题时,GC将失败,因为它不知道标题所在的指针的偏移。现在如何解决这个问题?
已经提到的一种方法是使用参考而不是可变的。另一种方法是使用getter和setter:
如果您只想
engry
可以传递增量器函数而不是getter/setter的变量。或任何其他辅助功能集合。 Getter/Setter Pait只是最通用的接口。Jeffrey Scofield explained why this can't be done in ocaml from the point of the type system.
But you can also look at it from the point of the GC (garbage collector). In ocaml internally everything is either a trivial type (int, bool, char, ...) that is stored as a 31/63 bit value or a pointer to a block of memory. Each block of memory has a header that describes the contents to the GC and has some extra bits used by GC.
When you look at a reference internally it is a pointer to the block of memory containing the record with a
mutable contents
. Through that pointger the GC can access the header and know the block of memory is still reachable.But lets just assume you could pass
apoint.y
to a function taking a reference. Then internally the pointer would point to the middle ofapoint
and the GC would fail when it tries to access the header of that block because it has no idea at what offset to the pointer the header is located.Now how to work around this?
One way that was already mentioned is to use references instead of mutable. Another way would be to use a getter and setter:
If you only want to
incr
the variable you can pass an incrementer function instead of getter/setter. Or any other collection of helper functions. A getter/setter pait is just the most generic interface.您始终可以暂时复制字段内容,在此函数再次调用该功能:
肯定不是尽可能高效(也不优雅),但可以正常工作。
You can always copy temporarily the content of field, call the function on that, and back again:
Certainly not as efficient (nor elegant) as it could, but it works.
不可能确切地做问题的问题(@JeffReyScofield解释了原因,所以我不会重复这一点)。建议一些解决方法。
如果您可以更改
增量
函数的实现,则可以使用这是另一个可行的解决方法。这非常接近所要求的。我们可以定义自己的参考类型,而不是让它进行“内置”参考。您可以设定并获得“参考”的精神。因此,我们可以将其描述/表示为
get
和set
函数的组合。我们可以在此类型上定义通常的
!
和:=
运算符:增量函数的代码甚至相同的'外观'相同(但是它是微妙的'不同的'因为现在使用我们自己的
ref
而不是内置参考)。当我们想要对一个字段的引用时,我们现在可以做一个。例如,一个可以引用X的函数:
现在我们可以在x字段上调用
增量
:It is impossible to do exactly what the question asks for (@JeffreyScofield explains why, so I won't repeat that). Some workarounds have been suggested.
Here is another workaround that might work if you can change the implementation of the
increment
function to use a 'home made' ref type. This comes very close to what was asked for.Instead of having it take a 'built-in' reference, we can define our own type of reference. The spirit of a 'reference' is something you can set and get. So we can characterise/represent it as a combination of a
get
andset
function.We can define the usual
!
and:=
operators on this type:The increment function's code can remain the same even its type 'looks' the same (but it is subtly 'different' as it is now using our own kind of
ref
instead of built-in ref).When we want a reference to a field we can now make one. For example a function to make a reference to x:
And now we can call
increment
on the x field: