Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Types 公共记录类型上的构造函数?_Types_Constructor_F#_Record - Fatal编程技术网

Types 公共记录类型上的构造函数?

Types 公共记录类型上的构造函数?,types,constructor,f#,record,Types,Constructor,F#,Record,假设我想要一个记录类型,例如: type CounterValues = { Values: (int) list; IsCorrupt: bool } 问题是,我想创建一个构造函数,它将传递的整数列表转换为一个没有负值的新列表(它们将被0替换),并且只有在构造时找到负值时,IsCorrupt=true F#能做到这一点吗 现在,这就是我所做的,使用属性(但是,meh,它不是很F#ish,它每次在getter调用ConvertAllNegativeValuesToZeroes(),所以它不是很

假设我想要一个记录类型,例如:

type CounterValues = { Values: (int) list; IsCorrupt: bool }
问题是,我想创建一个构造函数,它将传递的整数列表转换为一个没有负值的新列表(它们将被0替换),并且只有在构造时找到负值时,IsCorrupt=true

F#能做到这一点吗

现在,这就是我所做的,使用属性(但是,meh,它不是很F#ish,它每次在getter调用ConvertAllNegativeValuesToZeroes(),所以它不是很有效):

类型计数器值
(值:(int)列表)=
静态成员私有anynegativevalue
(值:(int)列表)
:bool=
将值与
|v::t->(v<0)| |计数器值。anynegativevalue(t)
|[]->false
静态成员私有ConvertAllNegativeValuesToZeroes
(值:(int)列表)
:(int)列表=
将值与
| [] -> []
|v::t->
如果(v<0),则
0::CounterValues.ConvertAllNegativeValuesToZeroes(t)
其他的
v::CounterValues.ConvertAllNegativeValuesToZeroes(t)
成员this.IsCorrupt=CounterValues.AnyNegativeValues(值)
请记住这个值
与get()一起
:(int)列表=
计数器值。将所有负数值转换为零(值)

不久前,我看了一些关于抽象数据类型(ADT)的内容,使用了这个构造。它对我很有效

type CounterValues = private { Values: int list; IsCorrupt: bool }
[<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
module CounterValues =

  let create values =
    let validated = 
      values 
      |> List.map (fun v -> if v < 0 then 0 else v)
    {Values = validated; IsCorrupt = validated <> values}

  let isCorrupt v =
    v.IsCorrupt

  let count v =
    List.length v.Values
type CounterValues=private{Values:int list;IsCorrupt:bool}
[]
模块计数器值=
让我们创造价值=
设有效=
价值观
|>List.map(乐趣v->如果v<0,则0其他v)
{Values=validated;IsCorrupt=validated Values}
让我来腐蚀你=
v、 腐败
让我数一数=
列表长度与值
CompliationRepresentation允许模块与类型具有相同的名称。
专用可访问性将阻止其他模块直接访问记录字段。您可以向CounterValues模块添加函数,以对传入的CounterValues类型进行操作和/或返回数据。注意我是如何添加两个函数isCorruptcount来处理CounterValues类型的

您不能有构造函数,但我通常看到使用的是静态工厂方法:

type CounterValues = 
    { Values: int list; IsCorrupt: bool }
    static member Make(values: int list) = 
         // do your work here, returning the constructed record.
而且,这是一个记录,而不是一个歧视性的联盟

编辑:我在评论中描述的内容如下:

type CounterValues = 
    { Values: int list }
    member this.IsCorrupt = 
        this.Values
        |> List.tryFind (fun x -> x < 0)
        |> Option.isSome
module MyDomainModel

type CounterValues = { Values : int list; IsCorrupt : bool }

let createCounterValues values =
    {
        Values = values |> List.map (max 0)
        IsCorrupt = values |> List.exists (fun x -> x < 0)
    }

let values cv = cv.Values

let isCorrupt cv = cv.IsCorrupt
// direct access fails
// let cv = API.CounterValues  // error

// using "factory" function
let cv1 = API.Create [1;2;3;4] 
cv1 |> API.Get    // ([1; 2; 3; 4], false)

let cv2 = API.Create [1;2;-3;4] 
cv2 |> API.Get    // ([1; 2; 0; 4], true)
类型计数器值=
{Values:int list}
成员this.IsCorrupt=
这就是价值观
|>List.tryFind(乐趣x->x<0)
|>选项1.isSome
这样,您的记录就有一个字段-
,您可以在使用标准语法构造记录时提供该字段
IsCorrupt
被编译为只读属性,当您访问它时将重新计算它,这意味着它不会与
值不同步,但与往常一样,会有一些折衷

假设您已经这样定义了您的模型:

type CounterValues = 
    { Values: int list }
    member this.IsCorrupt = 
        this.Values
        |> List.tryFind (fun x -> x < 0)
        |> Option.isSome
module MyDomainModel

type CounterValues = { Values : int list; IsCorrupt : bool }

let createCounterValues values =
    {
        Values = values |> List.map (max 0)
        IsCorrupt = values |> List.exists (fun x -> x < 0)
    }

let values cv = cv.Values

let isCorrupt cv = cv.IsCorrupt
// direct access fails
// let cv = API.CounterValues  // error

// using "factory" function
let cv1 = API.Create [1;2;3;4] 
cv1 |> API.Get    // ([1; 2; 3; 4], false)

let cv2 = API.Create [1;2;-3;4] 
cv2 |> API.Get    // ([1; 2; 0; 4], true)
请注意,声明的模块名称相同,但类型和函数仅在抽象中声明

因为
CounterValues
被定义为一个类型,但是没有任何特定的结构,所以任何客户端都不能创建它的实例。换句话说,这不会编译:

module Client

open MyDomainModel

let cv = { Values = [1; 2]; IsCorrupt = true }
module Client

let cv = MyDomainModel.createCounterValues [1; 2]
let v = cv |> MyDomainModel.values
let c = cv |> MyDomainModel.isCorrupt
编译器抱怨“记录标签‘值’未定义”

另一方面,客户端仍然可以访问签名文件定义的函数这将编译:

module Client

open MyDomainModel

let cv = { Values = [1; 2]; IsCorrupt = true }
module Client

let cv = MyDomainModel.createCounterValues [1; 2]
let v = cv |> MyDomainModel.values
let c = cv |> MyDomainModel.isCorrupt
以下是FSI的一些示例:

> createCounterValues [1; -1; 2] |> values;;
val it : int list = [1; 0; 2]

> createCounterValues [1; -1; 2] |> isCorrupt;;
val it : bool = true

> createCounterValues [1; 2] |> isCorrupt;;
val it : bool = false

> createCounterValues [1; 2] |> values;;
val it : int list = [1; 2]
> [1; -2; 3] |> replaceNegatives;;
val it : int list = [1; 0; 3]

> [1; -2; 3] |> isCorrupt;;
val it : bool = true

> [1; 2; 3] |> replaceNegatives;;
val it : int list = [1; 2; 3]

> [1; 2; 3] |> isCorrupt;;
val it : bool = false
缺点之一是保持签名文件(
.fsi
)和实现文件(
.fs
)同步会带来开销

另一个缺点是客户端无法自动访问记录的命名元素。相反,您必须定义并维护访问器函数,如
isCorrupt


尽管如此,这并不是F#中最常见的方法。一种更常见的方法是提供必要的函数来计算这些问题的答案:

module Alternative

let replaceNegatives = List.map (max 0)

let isCorrupt = List.exists (fun x -> x < 0)

这是凯文答案的变体,但是
计数器值
在模块中键入,并使用一次传递('List.foldBack')来 进行验证

module API =
    type CounterValues = private { Values: (int) list; IsCorrupt: bool }

    /// Create a CounterValues from a list of ints
    let Create intList =

        // helper for foldBack below
        let folder i (values,isCorrupt) =
            if i < 0 then 
                (0::values,true)
            else
                (i::values,isCorrupt)
        // one pass through the list to detect and fix bad values
        let newValues,isCorrupt = List.foldBack folder intList ([],false)

        // create a CounterValues 
        {Values=newValues; IsCorrupt=isCorrupt}

    /// Get the contents of a CounterValues 
    let Get cv =
        cv.Values, cv.IsCorrupt
但我和毛里西奥一样,认为布尔人不好。你有没有考虑过这样一种歧视性的工会类型

module API =
    type CounterValues = 
        private 
        | NonCorrupt of int list 
        | Corrupt of int list 

    /// Create a CounterValues from a list of ints
    let Create intList =

        // helper for foldBack below
        let folder i (values,isCorrupt) =
            if i < 0 then 
                (0::values,true)
            else
                (i::values,isCorrupt)
        // one pass through the list to detect and fix bad values
        let newValues,isCorrupt = List.foldBack folder intList ([],false)

        // create a CounterValues 
        if isCorrupt then Corrupt newValues else NonCorrupt newValues

    /// Get the contents of a CounterValues using active patterns
    let (|NonCorrupt|Corrupt|) cv =
        match cv with 
        | Corrupt intList -> Corrupt intList 
        | NonCorrupt intList -> NonCorrupt intList 

我还有一些做约束类型的例子。

最后,我选择了我最初提出的非有效解决方案、@Grundoon的折回算法和有效属性的混合,这些属性只是代理在构造时创建的值(因此它们不再是低效的,它们不会每次都得到评估):

类型计数器值
(值:(int)列表)=
//下面是折叠的助手
让文件夹v(值,isCorrupt)=
如果v<0,则
(0::value,true)
其他的
(v::值,isCorrupt)
//一次通过列表来检测和修复错误值
设CuratedValue,isCorrupt=
List.foldBack文件夹VAL([],false)
成员:this.IsCorrupt
与get()一起
:bool=
腐败
请记住这个值
与get()一起
:(int)列表=
策展价值观

这是最简单的解决方案,依我看。

但是这样一个开发人员可以创建一个计数器值实例,它的IsCorrupt=false,并且有负值,以防他忘记使用Make()。如果根据定义,IsCorrupt
总是从
字段派生,那么它首先就不应该是该记录的字段。改为将其作为记录的属性。是否编辑您的答案以反映您刚才所说的内容?使
IsCorrupt
成为一个经过计算的专业人士