我目前正在尝试将Scala包围起来,打算将其用于下一个必须处理DICOM的项目。 DICOM具有相当广泛的规范,涵盖了数千页的标准。我对DICOM的理解非常有限,但是总之DICOM对象-IOD(信息对象定义)由模块组成,而模块是类型化名称/值属性对的集合。一些模块和属性的可选性使情况更加复杂。例如:
SimpleImageIOD: {
PatientModule: {
name: String
dateOfBirth: DateTime
}
StudyModule: {
name: String
date: DateTime (optional)
}
SeriesModule: {
name: String
}
ImageModule: {
height: Integer
width: Integer
pixelSize: Double (optional)
}
EquipmentModule: { (optional)
type: String
}
}
有很多模块,它们可能组成各种组合,形成不同的IOD。反过来,Scala具有所有特征,案例类,动态类等的强大建模能力。您如何在Scala中为此类 Realm 建模?我是该语言的新手,但我一直在考虑使用不可变的案例类来定义模块,然后将它们聚合到各种IOD中,并使用镜头进行更新:
case class Patient(name: String, dateOfBirth: DateTime)
case class Study(name: String, date: Option[DateTime])
case class Series(name: String)
case class Image(height: Integer, width: Integer, pixelSize: Option[Double])
case class Equipment(type: String)
case class SimpleImageIOD(patient: Patient, study: Study, series: Series,
image: Image, equipment: Option[Equipment])
object Patient {
val nameL: Lens[Patient, String] = ...
val dateOfBirthL: Lens[Patient, DateTime] = ...
}
object SimpleImageIOD {
val patientL: Lens[SimpleImageIOD, Patient] = ...
val patientNamel = patientL andThen Patient.nameL
}
等等,对于镜头而言,对所有样板进行编码可能会成为一个问题-
M x N x L
镜头的顺序将覆盖M
IOD,N
模块和L
属性的整个 Realm 。此外,某些字段的可选性至少使scalaz-seven
极大地使任务复杂化。还有哪些其他可行的方法可以与Scala的精神相吻合?
最佳答案
您可以嵌套镜片,所以我不认为您总共会有M x N x L
镜片。我认为这是一种完全合理的方法。
另一种方法是定义自己的单项更新方法,如下所示:
case class Patient(name: String, dateOfBirth: Long) {
def name(f: String => String): Patient = copy(name = f(name))
def dateOfBirth(f: Long => Long): Patient = copy(dateOfBirth = f(dateOfBirth))
}
您可以这样使用:
scala> Patient("John",0).name(_ + " Smith")
res1: Patient = Patient(John Smith,0)
使用镜头可以实现这一点,但是您必须非常小心,以免拥有更多样板(在用法和清晰度方面)。它没有镜头的灵活性,您可以随意拉开镜头的功能(例如更新),但是通过精简来弥补这一点。
我以这种方式进行了大部分深度更新:
myImage.patient(_.study(_.date(_ => Some(System.currentTimeMillis))))
是您需要用当前时间的研究来更新整个树的全部内容(在这里假装
DateTime
实际上是Long
),并且如果您确实需要提取这种再生此针状树的能力(对于镜头,这更自然) ), 你可以val setStudyTime = (t: Long) => myImage.patient(_.study(_.date(_ => Some(t))))
甚至
def studyTimeSetter(si: SimpleImageIOD) =
(t: Long) => si.patient(_.study(_.date(_ => Some(t))))
如果您需要立即轻松地获得此功能。
如果您需要镜头提供的所有功能,则需要使用此方法以临时方式对其进行重建,但是如果您只需要一小部分并且主要只需要减少样板进行设置,那么这样做会非常好工作。