2013-03-23 6 views
7

В настоящее время я пытаюсь обратить внимание на Scala с намерением использовать его для моего следующего проекта, который должен иметь дело с DICOM. DICOM имеет довольно широкую спецификацию, которая охватывает более тысячи страниц стандарта. Мое понимание DICOM довольно ограничено, но вкратце DICOM-объекты - IOD (определение информационного объекта) состоят из модулей, а модули - это набор пар атрибутов имени-значения. Это дополнительно осложняется опциональностью некоторых модулей и атрибутов. Например:Scala - Моделирование объектов домена

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 
    } 
} 

Есть тонны модулей, они могут быть составлены в различных комбинациях, образующих разные ИОД. В свою очередь, Scala имеет множество возможностей моделирования со всеми чертами, классами классов, динамическими классами и т. Д. Как бы вы могли моделировать такой домен в Scala? Я совершенно новый для языка, но я думал использовать неизменяемые классы регистра для определения модулей, а затем объединить их в различном IODs и использовать линзы для обновления:

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 
} 

Etc и т.д. Что касается линз это может стать проблемой для кодирования всего шаблона - для объективов M x N x L будет создан объектив для покрытия всего домена для M IOD, N и L. Также опциональность некоторых полей усложняет задачу, по крайней мере, с scalaz-seven.

Какие существуют другие жизнеспособные подходы, которые согласуются с духом Scala?

ответ

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 на самом деле Long), и если вам действительно нужно, чтобы извлечь эту способность регенерации этого paticular дерево (которое является более естественным с линзами), вы могли бы

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

или даже

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

, если вам необходимо с легкостью воспользоваться этой возможностью в любой момент.

Если вам нужно все, что предлагают вам объективы, то это больше поможет перестроить его специальным способом, используя этот подход, но если вам нужна только небольшая часть и в основном нужно просто сбить шаблон для установки, это делает довольно хорошую работу.

+0

Что вы подразумеваете под «гнездовыми линзами»? –

+0

@ak. - Ну, «состари» действительно. Если у вас есть что-то вроде «Long => Patient» и «Patient => SimpleImageIOD», вы можете составить их, чтобы получить «Long => SimpleImageIOD». Вам не нужно строить каждую возможность; вы можете сделать это там, где вы их используете. –

+0

Что касается линз scalaz, вы, вероятно, имеете в виду 'andThen', а не' compose'. Я думаю, что 'compose' применяется к двум объективам' Long => Patient' и 'String => Patient' будет производить объектив, который делает' (Long, String) => Patient'. Но дело не в том, чтобы ловить линзы на шаблоне, верно? –

Смежные вопросы