• F#奇妙游(31):数据缺失的处理


    option

    在处理数据时,缺失数据和无效数据是一个常见的问题。在Python中,通常使用None来表示缺失数据。

    在F#中,我们可以使用FSharpOption来表示缺失数据。FSharpOption是一个泛型类型,它有两个值:Some和None。Some表示一个值,而None表示一个缺失的值。FSharpOption的定义如下:

    type FSharpOption =
        | None
        | Some of T
    
    • 1
    • 2
    • 3

    从ADT的组合数来分析,这个和类型的组合数是 1 + C T 1+C_T 1+CT 。在通常的情况下,我们在F#中都把这个写作option,或者T option。而构造一个option的值,我们可以使用SomeNone这两个构造器,例如:

    let x = Some 1
    let y = None
    
    • 1
    • 2

    一般的,我们可以使用match表达式来处理option的值,例如:

    let z = 
        match x with
        | Some x -> x
        | None -> 0
    
    • 1
    • 2
    • 3
    • 4

    如果是定义输入参数是option类型的函数,还有一个语法糖:

    let f (x: int option) =
        match x with
        | Some x -> x
        | None -> 0
    
    • 1
    • 2
    • 3
    • 4

    可以写成:

    let f = 
        function
        | Some x -> x
        | None -> 0
    
    • 1
    • 2
    • 3
    • 4

    后面这个情况非常适用于配合|>操作符使用,例如:

    let x = Some 1
    x |> (function Some x -> x | None -> 0)
    
    • 1
    • 2

    在对一个T option的集合类型时,我们就可以很方便的用上面的语法糖来构造一些小的判定函数。

    FSharp.Core.Option

    而在实际的工作中,上面的这些都是不需要的,因为F#已经提供了FSharp.Core.Option模块来处理option类型的值。下表按照ADT分析的结果,列出了FSharp.Core.Option模块中的函数:

    Function or valueADTDescription
    isSome option('a option -> bool)返回 true, 如果对象不是None.
    isNone option('a option -> bool)返回 true, 如果对象是None.
    count option('a option -> int)count inp 展开成为 match inp with None -> 0 \| Some _ -> 1.
    get option('a option -> 'a)得到option所包含的值.
    toObj value('a option -> 'a)转换成一个对象.
    toNullable option('a option -> System.Nullable<'a>)转换成Nullable值.
    toArray option('a option -> 'a array)转换成一个数组,长度是0或者1.
    toList option('a option -> 'a list)转换成一个列表,长度是0或者1.
    ofObj value('a -> 'a option)转换成一个option,空值为None.
    ofNullable value(System.Nullable<'a> -> 'a option)转换成一个option,null变为None.
    flatten option('a option option -> 'a option)flatten inp 展开成为 match inp with None -> None\| Some x -> x
    contains value option('a -> 'a option -> bool)如果option为Some value返回true否则返回false.
    forall predicate option(('a -> bool) -> 'a option -> bool)forall p inp 展开成为 match inp with None -> true\| Some x -> p x.
    exists predicate option(('a -> bool) -> 'a option -> bool)exists p inp 展开成为 match inp with None -> false\| Some x -> p x.
    defaultValue value option('a -> 'a option -> 'a)如果对象Some value得到对应value, 否则返回默认值.
    defaultWith defThunk option((unit -> 'a) -> 'a option -> 'a)如果对象Some value得到对应value, 否则求值defThunk并返回结果.
    iter action option(('a -> unit) -> 'a option -> unit)iter f inp 展开成为 match inp with None -> ()\| Some x -> f x.
    orElse ifNone option('a option -> 'a option -> 'a option)如果option为Some value直接返回, 否则返回ifNone.
    bind binder option(('a -> 'b option) -> 'a option -> 'b option)bind f inp 展开成为 match inp with None -> None \| Some x -> f x
    filter predicate option(('a -> bool) -> 'a option -> 'a option)filter f inp 展开成为 match inp with None -> None\| Some x -> if f x then Some x else None.
    map mapping option(('a -> 'b) -> 'a option -> 'b option)map f inp 展开成为 match inp with None -> None\| Some x -> Some (f x).
    orElseWith ifNoneThunk option((unit -> 'a option) -> 'a option -> 'a option)如果option为Some value直接返回, 否则求值ifNoneThunk并返回结果.
    fold folder state option(('a -> 'b -> 'a) -> 'a -> 'b option -> 'a)fold f s inp 展开成为 match inp with None -> s\| Some x -> f s x.
    foldBack folder option state(('a -> 'b -> 'b) -> 'a option -> 'b -> 'b)fold f inp s 展开成为 match inp with None -> s\| Some x -> f x s.
    map2 mapping option1 option2(('a -> 'b -> 'c) -> 'a option -> 'b option -> 'c option)map f option1 option2 展开成为 match option1, option2 with Some x, Some y -> Some (f x y)\| _ -> None.
    map3 mapping option1 option2 option3(('a -> 'b -> 'c -> 'd) -> 'a option -> 'b option -> 'c option -> 'd option)map f option1 option2 option3 展开成为 match option1, option2, option3 with Some x, Some y, Some z -> Some (f x y z) \| _ -> None.

    其实这许多的功能,只需要略微看看源代码就知道,基本上就是替换为对应match表达式的代码。例如isSome的源代码如下:

    let inline isSome option =
        match option with
        | None -> false
        | Some _ -> true
    
    • 1
    • 2
    • 3
    • 4

    这样的好处是,可以对一个option的集合类型使用mapfilter等函数,例如:

    let x = [Some 1; None; Some 2]
    x |> List.filter Option.isSome
    
    • 1
    • 2

    便于写出非常简洁的代码。

    结论

    1. 在F#中,使用option来表示缺失数据;
    2. 使用FSharp.Core.Option模块来处理option类型的值;
    3. 使用|>操作符配合function语法糖来处理option类型的值。

    Option源代码

    namespace Microsoft.FSharp.Core
    
    open Microsoft.FSharp.Core.Operators
    
    []
    module Option =
    
        []
        let get option =
            match option with
            | None -> invalidArg "option" (SR.GetString(SR.optionValueWasNone))
            | Some x -> x
    
        []
        let inline isSome option =
            match option with
            | None -> false
            | Some _ -> true
    
        []
        let inline isNone option =
            match option with
            | None -> true
            | Some _ -> false
    
        []
        let inline defaultValue value option =
            match option with
            | None -> value
            | Some v -> v
    
        []
        let inline defaultWith ([] defThunk) option =
            match option with
            | None -> defThunk ()
            | Some v -> v
    
        []
        let inline orElse ifNone option =
            match option with
            | None -> ifNone
            | Some _ -> option
    
        []
        let inline orElseWith ([] ifNoneThunk) option =
            match option with
            | None -> ifNoneThunk ()
            | Some _ -> option
    
        []
        let inline count option =
            match option with
            | None -> 0
            | Some _ -> 1
    
        []
        let inline fold<'T, 'State> ([] folder) (state: 'State) (option: 'T option) =
            match option with
            | None -> state
            | Some x -> folder state x
    
        []
        let inline foldBack<'T, 'State> ([] folder) (option: option<'T>) (state: 'State) =
            match option with
            | None -> state
            | Some x -> folder x state
    
        []
        let inline exists ([] predicate) option =
            match option with
            | None -> false
            | Some x -> predicate x
    
        []
        let inline forall ([] predicate) option =
            match option with
            | None -> true
            | Some x -> predicate x
    
        []
        let inline contains value option =
            match option with
            | None -> false
            | Some v -> v = value
    
        []
        let inline iter ([] action) option =
            match option with
            | None -> ()
            | Some x -> action x
    
        []
        let inline map ([] mapping) option =
            match option with
            | None -> None
            | Some x -> Some(mapping x)
    
        []
        let inline map2 ([] mapping) option1 option2 =
            match option1, option2 with
            | Some x, Some y -> Some(mapping x y)
            | _ -> None
    
        []
        let inline map3 ([] mapping) option1 option2 option3 =
            match option1, option2, option3 with
            | Some x, Some y, Some z -> Some(mapping x y z)
            | _ -> None
    
        []
        let inline bind ([] binder) option =
            match option with
            | None -> None
            | Some x -> binder x
    
        []
        let inline flatten option =
            match option with
            | None -> None
            | Some x -> x
    
        []
        let inline filter ([] predicate) option =
            match option with
            | None -> None
            | Some x -> if predicate x then Some x else None
    
        []
        let inline toArray option =
            match option with
            | None -> [||]
            | Some x -> [| x |]
    
        []
        let inline toList option =
            match option with
            | None -> []
            | Some x -> [ x ]
    
        []
        let inline toNullable option =
            match option with
            | None -> System.Nullable()
            | Some v -> System.Nullable(v)
    
        []
        let inline ofNullable (value: System.Nullable<'T>) =
            if value.HasValue then
                Some value.Value
            else
                None
    
        []
        let inline ofObj value =
            match value with
            | null -> None
            | _ -> Some value
    
        []
        let inline toObj value =
            match value with
            | None -> null
            | Some x -> x
    
    module ValueOption =
    
        []
        let get voption =
            match voption with
            | ValueNone -> invalidArg "option" (SR.GetString(SR.optionValueWasNone))
            | ValueSome x -> x
    
        []
        let inline isSome voption =
            match voption with
            | ValueNone -> false
            | ValueSome _ -> true
    
        []
        let inline isNone voption =
            match voption with
            | ValueNone -> true
            | ValueSome _ -> false
    
        []
        let inline defaultValue value voption =
            match voption with
            | ValueNone -> value
            | ValueSome v -> v
    
        // We're deliberately not using InlineIfLambda, because benchmarked code ends up slightly slower at the time of writing (.NET 8 Preview)
        []
        let inline defaultWith defThunk voption =
            match voption with
            | ValueNone -> defThunk ()
            | ValueSome v -> v
    
        []
        let inline orElse ifNone voption =
            match voption with
            | ValueNone -> ifNone
            | ValueSome _ -> voption
    
        []
        let inline orElseWith ([] ifNoneThunk) voption =
            match voption with
            | ValueNone -> ifNoneThunk ()
            | ValueSome _ -> voption
    
        []
        let inline count voption =
            match voption with
            | ValueNone -> 0
            | ValueSome _ -> 1
    
        []
        let inline fold<'T, 'State> ([] folder) (state: 'State) (voption: voption<'T>) =
            match voption with
            | ValueNone -> state
            | ValueSome x -> folder state x
    
        []
        let inline foldBack<'T, 'State> ([] folder) (voption: voption<'T>) (state: 'State) =
            match voption with
            | ValueNone -> state
            | ValueSome x -> folder x state
    
        []
        let inline exists ([] predicate) voption =
            match voption with
            | ValueNone -> false
            | ValueSome x -> predicate x
    
        []
        let inline forall ([] predicate) voption =
            match voption with
            | ValueNone -> true
            | ValueSome x -> predicate x
    
        []
        let inline contains value voption =
            match voption with
            | ValueNone -> false
            | ValueSome v -> v = value
    
        []
        let inline iter ([] action) voption =
            match voption with
            | ValueNone -> ()
            | ValueSome x -> action x
    
        []
        let inline map ([] mapping) voption =
            match voption with
            | ValueNone -> ValueNone
            | ValueSome x -> ValueSome(mapping x)
    
        []
        let inline map2 ([] mapping) voption1 voption2 =
            match voption1, voption2 with
            | ValueSome x, ValueSome y -> ValueSome(mapping x y)
            | _ -> ValueNone
    
        []
        let inline map3 ([] mapping) voption1 voption2 voption3 =
            match voption1, voption2, voption3 with
            | ValueSome x, ValueSome y, ValueSome z -> ValueSome(mapping x y z)
            | _ -> ValueNone
    
        []
        let inline bind ([] binder) voption =
            match voption with
            | ValueNone -> ValueNone
            | ValueSome x -> binder x
    
        []
        let inline flatten voption =
            match voption with
            | ValueNone -> ValueNone
            | ValueSome x -> x
    
        []
        let inline filter ([] predicate) voption =
            match voption with
            | ValueNone -> ValueNone
            | ValueSome x ->
                if predicate x then
                    ValueSome x
                else
                    ValueNone
    
        []
        let inline toArray voption =
            match voption with
            | ValueNone -> [||]
            | ValueSome x -> [| x |]
    
        []
        let inline toList voption =
            match voption with
            | ValueNone -> []
            | ValueSome x -> [ x ]
    
        []
        let inline toNullable voption =
            match voption with
            | ValueNone -> System.Nullable()
            | ValueSome v -> System.Nullable(v)
    
        []
        let inline ofNullable (value: System.Nullable<'T>) =
            if value.HasValue then
                ValueSome value.Value
            else
                ValueNone
    
        []
        let inline ofObj value =
            match value with
            | null -> ValueNone
            | _ -> ValueSome value
    
        []
        let inline toObj value =
            match value with
            | ValueNone -> null
            | ValueSome x -> x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
  • 相关阅读:
    设计模式从哪来、难学吗、什么时候学、能做什么?(设计模式与开发实践 P1)
    优选丨 5 大用例设计笔试大题,附超详细解析
    【MATLAB】 01 基本操作与数组输入
    Java制作罗盘
    池式结构:对象池(Object Pool)
    【蓝桥杯国赛真题24】Scratch货物运输 第十三届蓝桥杯 图形化编程scratch国赛真题和答案讲解
    【数据结构】线性表相关操作(C++实现)
    消息队列常见问题
    PythonStudy5
    数据结构之时空复杂度
  • 原文地址:https://blog.csdn.net/withstand/article/details/132779805