我目前正在尝试将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))))

如果您需要立即轻松地获得此功能。

如果您需要镜头提供的所有功能,则需要使用此方法以临时方式对其进行重建,但是如果您只需要一小部分并且主要只需要减少样板进行设置,那么这样做会非常好工作。

07-24 20:10