crystal-lang - 如何在 Crystal 中进行通用记忆?
问题描述
我想在 Crystal 中定义一个通用的记忆包装器。我有以下水晶代码:
module Scalar(T)
abstract def value: T
end
class ScSticky(T)
include Scalar(T)
def initialize(sc : Scalar(T))
@sc = sc
@val = uninitialized T
end
def value: T
@val ||= @sc.value
end
end
换句话说,我只想ScSticky
调用底层Scalar(T)
并返回所有后续调用的缓存输出。T
但是,如果是,上述方法不起作用Int32
例如,包装此类时
class ScCounter
include Scalar(Int32)
def initialize
@val = 100
end
def value: Int32
@val += 1
@val
end
end
ScSticky(ScCounter.new).value
将始终等于0
(据我了解,因为unitialized Int32
实际上是用 0 值初始化的)
我非常感谢您对这个问题的帮助
Upd:似乎实现这一点的正确方法是使用nil
,但是我在理解这种实现的外观方面存在问题。我也希望能够记住.value
方法,即使它返回nil
(换句话说,如果它T
是可空类型)
解决方案
您正在使用一个不安全的功能“ uninitialized
”,这意味着,“保留以前内存中的任何内容”(理论上该值是随机的并且可能无效,实际上您通常最终还是 0 - 但它仍然不能保证)。
关于该uninitialized
功能的简短故事是请不要使用它。
如果你写了这种行为,你不会感到惊讶@val = 0
——这就是你写的。
您必须定义@val : T? = nil
-- 以使其可空(具有这个单独的可能值nil
,这是它自己的类型 - Nil
)。
你可能认为这unitialized
会带来nil
画面,但它绝对不会。
作为对您关于还包括nil
可能值的评论的回应,这里有一个完整的解决方案,它使用用户永远无法创建的唯一“哨兵”结构,而不是 Nil。
module Scalar(T)
abstract def value: T
end
private struct Sentinel
end
class ScSticky(T)
include Scalar(T)
@val : T | Sentinel = Sentinel.new
def initialize(@sc : Scalar(T))
end
def value: T
val = @val
if val.is_a?(Sentinel)
@val = @sc.value
else
val
end
end
end
class ScCounter
include Scalar(Int32)
def initialize
@val = 100
end
def value: Int32
@val += 1
end
end
sc = ScSticky.new(ScCounter.new)
p! sc.value #=> 101
p! sc.value #=> 101
推荐阅读
- machine-learning - 深度神经网络模型在一个 epoch 后停止学习
- batch-file - 无明显原因关闭批处理文件
- python - 如何在tensorflow中重用操作?
- python - 具有实际环境而不是 python 类环境的 TensorFlow 代理
- apache-kafka - 删除 Kafka/log 旧文件是否安全?
- python - 如何让我的函数在以下代码中在 tkinter 中配置正确的标签?
- javascript - 如何自动化应用程序脚本中的特定范围
- java - 错误:不兼容的类型:从 double 到 float 的可能有损转换
- ios - 如何从 Swift 包管理器清单文件加载文件并运行 swift 代码?
- mongodb - Strapi mongo颤振应用程序连接错误