F#-重量(计量单位)石头磅

F#-重量(计量单位)石头磅,f#,units-of-measurement,F#,Units Of Measurement,我有一个包含每日重量的csv文件,如下所示: Date,Name,Weight 11-Sep-2017,Alpha,9-1 13-Sep-2017,Alpha,8-13 15-Sep-2017,Alpha,8-11 // http://fssnip.net/27 let lazySplit (sep:string) (str:string) = match sep, str with | ((null | ""), _) | (_, (null | "")) -> seq

我有一个包含每日重量的csv文件,如下所示:

Date,Name,Weight
11-Sep-2017,Alpha,9-1
13-Sep-2017,Alpha,8-13
15-Sep-2017,Alpha,8-11
// http://fssnip.net/27
let lazySplit (sep:string) (str:string) =
    match sep, str with
    | ((null | ""), _) | (_, (null | "")) -> seq [str]
    | _ ->
      let n, j = str.Length, sep.Length
      let rec loop p =
        seq {
          if p < n then
            let i = match str.IndexOf(sep, p) with -1 -> n | i -> i
            yield str.Substring(p, i - p)
            yield! loop (i + j)
        }
      loop 0

let weight input =
    input
    |> (fun x -> lazySplit "/" x |> Seq.take 2 |> String.concat("-"))

let data = Weighings.GetSample()
for row in data.Rows do
    let stlbs = weight (string row.Weight)
    printfn "Output: (%A, %A, %A)" row.Date row.Name stlbs

// Output: 11-Sep-2017,"Alpha","09-01")
虽然我可以使用CsvProvider成功导入它们,但权重列默认为System.DateTime

    // Weight
    [<Measure>] type lb

    [<Literal>]
    let input = "DayWeights.csv"
    type Weights = CsvProvider<input, HasHeaders=true>

    let data = Weights.GetSample()
    for row in data.Rows do
        printfn "Output: (%A, %A, %A)" row.Date row.Name row.Weight
//重量
[]lb型
[]
让输入=“DayWeights.csv”
类型权重=CsvProvider
让数据=权重。GetSample()
用于数据中的行。行执行
printfn“输出:(%A,%A,%A)”行。日期行。名称行。重量

是否可以创建一个计量单位(UoM)来定义“stlb”,并在导入时选择转换为lbs,如果可以,如何转换?

我认为您不能将磅表示为一个单一的数字类型,并且计量单位只能用于数字类型(尽管将来会对此进行更改)。这是因为它们的一些特性只在加法和乘法等数字运算中才有意义。单位本身被乘和除:

[<Measure>] type lb
2<lb> + 2<lb> // 4<lb>
2<lb> * 2<lb> // 4<lb ^ 2>
2<lb> / 2<lb> // 1
[]类型lb
2 + 2 // 4
2 * 2 // 4
2 / 2 // 1
如果希望某种类型的标记知道给定的值有一种类型,而不是度量单位,则可以创建一个区分大小写的并集:

type StonesPounds = StonesPounds of int * int

// StonesPounds -> int<lb>
let convertToLb (StonesPounds (s, p)) = (s * 14 + p) * 1<lb>

StonesPounds (1, 2) |> convertToLb // 16<lb>
类型stonespoonds=int*int的stonespoonds
//石头嘴->内部
设convertToLb(StonesPounds(s,p))=(s*14+p)*1
StonesPounds(1,2)|>敞篷车B//16

与度量单位相比,这种方法的缺点是,在使用数字之前,您必须在代码中手动打包和解包这些值,这也需要运行时成本。

我解决了将输入权重列自动转换为System.DateTime的问题,如下所示:

Date,Name,Weight
11-Sep-2017,Alpha,9-1
13-Sep-2017,Alpha,8-13
15-Sep-2017,Alpha,8-11
// http://fssnip.net/27
let lazySplit (sep:string) (str:string) =
    match sep, str with
    | ((null | ""), _) | (_, (null | "")) -> seq [str]
    | _ ->
      let n, j = str.Length, sep.Length
      let rec loop p =
        seq {
          if p < n then
            let i = match str.IndexOf(sep, p) with -1 -> n | i -> i
            yield str.Substring(p, i - p)
            yield! loop (i + j)
        }
      loop 0

let weight input =
    input
    |> (fun x -> lazySplit "/" x |> Seq.take 2 |> String.concat("-"))

let data = Weighings.GetSample()
for row in data.Rows do
    let stlbs = weight (string row.Weight)
    printfn "Output: (%A, %A, %A)" row.Date row.Name stlbs

// Output: 11-Sep-2017,"Alpha","09-01")
//http://fssnip.net/27
让懒散(sep:string)(str:string)=
将sep、str与
|((null |“”),(null |“”)->seq[str]
| _ ->
设n,j=str.Length,sep.Length
让rec循环p=
序号{
如果pn | i->i匹配
产量str.Substring(p,i-p)
屈服!循环(i+j)
}
循环0
让权重输入=
输入
|>(乐趣x->lazyspilt“/”x |>Seq.take 2 |>String.concat(“-”)
让数据=称重。GetSample()
用于数据中的行。行执行
设stlbs=重量(字符串行.重量)
printfn“输出:(%A,%A,%A)”行。日期行。名称stlbs
//产出:2017年9月11日,“阿尔法”,“09-01”)

感谢您的专家帮助和指导。

您没有指定任何类型,因此类型提供程序必须推断类型。为什么要使用未知的
lb
测量值?顺便问一下,为什么人类会这样做
-
在任何类型的数值中都不用作分隔符不幸的是,我无法控制csv文件的格式。它不是该文件。提供商无法通过查看数据猜出您想要使用
lb
。您必须告诉它要使用哪种类型如果我可以将权重字段作为字符串导入,而不必进行自动System.DateTime转换,那将有所帮助。谢谢您的清晰解释和指导。如何防止输入csv列自动转换为System.DateTime?请参阅的“控制列类型”部分。您可能需要创建自己的小示例文件,其中包含在创建时传递给类型提供程序的类型提示,然后您可以加载真实文件以处理真实数据。我建议将您尝试过的内容作为单独的问题发布。