Functional programming CAL中的用户定义记录类型?
我怀疑这是一个简单的问题。我有一个简单的函数定义Functional programming CAL中的用户定义记录类型?,functional-programming,Functional Programming,我怀疑这是一个简单的问题。我有一个简单的函数定义 makePatientFixture :: [ { name :: String, age :: Int} ]; makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ]; 我实际上想定义一个新类型,名为 Patient = { name :: String, age :: Int
makePatientFixture :: [ { name :: String, age :: Int} ];
makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ];
我实际上想定义一个新类型,名为
Patient = { name :: String, age :: Int }
这意味着我不必一直重复记录结构({name::String,age::Int}),相反,我的代码如下所示:
makePatientFixture :: [ Patient ];
makePatientFixture = [ { name = "Dave", age = 41}, { name = "Denise", age = 45}, { name = "Cameron", age = 5} ];
这可能吗?从CAL的角度来看,它有意义吗(可能没有)?CAL不支持别名(Haskell使用'type'关键字实现了这一点),因此您不能只执行以下操作:
type Patient = {name::String, age::Int}
但是,您可以创建包含记录的新数据类型:
data Patient=
public Patient
details :: {name::String, age::Int}
;
。。。然而,这可能不是您需要的。记录对于移动结构化数据位和使用结构化多态性(记录子集的自动投影)非常方便。不过,您不需要像这样存储数据。
相反,我建议:
data Patient=
public Patient
name :: !String
age :: !Int
;
类型上的“plings”表示“不必在这里存储懒散的thunk”,例如,我们确实需要一个字符串和int,即使您将一些复杂的表达式应用于患者构造函数。您可以安全地省略这些扣环,但在大多数情况下,最好将它们包括在内
现在,您可以使用各种形式的案例分析从此类患者价值中提取元素。您将在手册中看到所有这些,但这里有一个摘要:
公开案例分析,位置匹配:
公开案例分析,符号匹配:
惰性提取器
单箱提取器
如果需要,您始终可以从此数据投影记录。
请记住,如果存在多种类型的患者,那么患者数据类型也可以有多个构造函数
例如,可能有住院病人和门诊病人。两者都有一些nhs患者记录,但都有与治疗相关的特定领域。
我们可以用以下几条线来表示:
data Patient=
public InPatient
patientRecords :: !PatientRecord
careCentreID :: !Int
department :: !String
consultant :: !String
| publicOutPatient
patientRecords :: !PatientRecord
appointments :: ![Appointment]
;
nhsRecords p =
case p of
(InPatient|OutPatient) {patientRecords} -> patientRecords;
;
这也让我们可以看到一个非常强大的CAL功能,它可以进行多个构造函数匹配。在本例中,我们匹配住院患者和门诊患者,仅投影patientRecords字段
这使我们能够编写一个“nhsRecords”提取程序函数,即使患者构造器中的细节发生变化,我们也可以相当轻松地维护该函数。事实上,除非构造函数来来去去,或者“patientRecords”字段本身发生了什么事情,否则此函数永远不需要更改
nameAndAge p =
case p of
Patient{name,age} -> (name,age); // Now it doesn't matter if the Patient constructor grows new arguments
;
name p =
let
Patient{name} = p; // name is only extracted (and p partially evaluated) if name is required
in
name;
name p =
p.Patient.name; // Syntactic sugar for example 1. Useful when you are _only_ extracting a single field.
data Patient=
public InPatient
patientRecords :: !PatientRecord
careCentreID :: !Int
department :: !String
consultant :: !String
| publicOutPatient
patientRecords :: !PatientRecord
appointments :: ![Appointment]
;
nhsRecords p =
case p of
(InPatient|OutPatient) {patientRecords} -> patientRecords;
;