7

我目前正试图围绕 Scala 进行思考,打算将其用于我的下一个必须处理 DICOM 的项目。DICOM 具有相当广泛的规范,跨越标准的数千页。我对DICOM的理解相当有限,但总之DICOM对象——IOD(Information Object Definition)是由Modules组成的,Modules是typed name-value属性对的集合。由于某些模块和属性的可选性,它变得更加复杂。例如:

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 中对这样的领域进行建模?我对这门语言还很陌生,但我一直在考虑使用不可变的案例类来定义模块,然后将它们聚合到各种 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
}

等等。关于镜头,编码所有样板文件可能会成为一个问题——镜头的顺序将覆盖IOD、模块和属性M x N x L的整个域。此外,某些领域的可选性至少使任务变得非常复杂。MNLscalaz-seven

那里还有哪些符合 Scala 精神的可行方法?

4

1 回答 1

5

你可以嵌套镜头,所以我认为你不会有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实际上是 a Long),如果你真的需要提取这种再生这棵特殊树的能力(这对于镜头来说更自然),你可以

val setStudyTime = (t: Long) => myImage.patient(_.study(_.date(_ => Some(t))))

甚至

def studyTimeSetter(si: SimpleImageIOD) =
  (t: Long) => si.patient(_.study(_.date(_ => Some(t))))

如果您需要立即轻松掌握此功能。

如果您需要镜头为您提供的所有东西,使用这种方法以临时方式重建它需要更多的工作,但如果您只需要一小部分并且大部分只需要减少样板进行设置,这会非常好工作。

于 2013-03-23T20:34:38.850 回答