【问题标题】:F# - Weight (Unit of Measure) Stones-PoundsF# - 重量(计量单位)石头-磅
【发布时间】:2018-03-25 08:59:50
【问题描述】:

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

Date,Name,Weight
11-Sep-2017,Alpha,9-1
13-Sep-2017,Alpha,8-13
15-Sep-2017,Alpha,8-11

虽然我可以使用 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

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

【问题讨论】:

  • 你没有指定任何类型,所以类型提供者必须推断类型。为什么要使用 unknown lb 度量?顺便说一句,人类为什么会这样? - 不用作任何数值的分隔符
  • 很遗憾,我无法控制 csv 文件的格式。
  • 不是文件。提供者无法通过查看数据来猜测您想要使用lb。你必须告诉它使用哪种类型
  • 如果我可以将权重字段作为字符串导入并且不会遭受 System.DateTime 自动转换,这会有所帮助吗?

标签: f# units-of-measurement


【解决方案1】:

我认为您不能将石头-磅表示为单一的数字类型,并且计量单位只能用于数字类型(尽管有 some discussion 关于将来更改它)。这是因为它们的某些功能仅对加法和乘法等数字运算有意义。单位本身是乘除法:

[<Measure>] type lb
2<lb> + 2<lb> // 4<lb>
2<lb> * 2<lb> // 4<lb ^ 2>
2<lb> / 2<lb> // 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>

与度量单位相比,这样做的缺点是您必须在代码中手动打包和解包这些值,然后才能使用这些数字,而且这也会产生运行时成本。

【讨论】:

  • 感谢清晰的解释和指导。如何防止输入 csv 列的自动转换为 System.DateTime?
  • 查看this page 的“控制列类型”部分。您可能需要制作自己的小示例文件,其中包含在创建时传递给类型提供程序的类型提示,然后您可以加载真实文件以使用真实数据。
  • 我建议将您尝试过的内容作为一个单独的问题发布。
【解决方案2】:

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

// 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")

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

【讨论】:

    猜你喜欢
    • 2011-09-13
    • 1970-01-01
    • 2010-09-11
    • 2013-03-12
    • 2019-11-12
    • 2010-09-07
    • 2010-10-02
    • 2012-07-05
    • 1970-01-01
    相关资源
    最近更新 更多