【问题标题】:Defining tuple data types in the function signature on F#在 F# 的函数签名中定义元组数据类型
【发布时间】:2018-12-12 15:41:11
【问题描述】:

我想定义一个元组元素的预期数据类型,它将被传递给一个函数。当我不定义它并让类型推断工作时,它是可以的,但是在我想编写仍然没有在任何地方调用的小函数时,我不知道如何定义参数。

这个例子。我希望description 是一个元组,其中五个元素中的每一个都是int,然后提取每个部分以使用。

let setArray (description: int * int * int * int * int)  =
    let id = fun (i, _, _, _, _) -> i
    let startX = fun (_, x, _, _, _) -> x
    let startY = fun (_, _, y, _, _) -> y
    let width = fun (_, _, _, w, _) -> w
    let height = fun (_, _, _, _, h) -> h
    let arrayResult = Array2D.init (width + 1) (height + 1) (fun i j -> if i < width &&  j < height then id)
    arrayResult

.fsx(29,45): 错误 FS0001: 类型 'int' 与类型 ''a * 'b * 'c * 'd * 'e -> 'd' 不匹配

对于其他功能,就像我说的,类型推断有效,我可以毫无问题地使用模式匹配

let getMaxCoords =
    let x = elements |> Seq.map (fun (_, x, _, _, _) -> x) |> Seq.max
    let y = elements |> Seq.map (fun (_, _, y, _, _) -> x) |> Seq.max
    x, y

我做错了什么?

【问题讨论】:

  • 错误是由Array2D.init之后的代码引发的,而不是签名。他们抱怨是因为idstartX 等是适用于元组而不是整数的函数。你可以写height description 并从description 获取最后一个元素
  • width 是一个函数,所以 (width + 1) 是一个类型错误。看起来您想将元组参数解构为setArray,即let setArray (id, startX, startY, width, height)) = ...。如果你这样做,你传递给 Array2D.init 的 init 函数需要在 else 分支中返回一个值。

标签: f# tuples


【解决方案1】:

首先,下面的代码块定义了五个函数,而不是五个整数值:

let setArray (description: int * int * int * int * int)  =
    let id = fun (i, _, _, _, _) -> i
    let startX = fun (_, x, _, _, _) -> x
    let startY = fun (_, _, y, _, _) -> y
    let width = fun (_, _, _, w, _) -> w
    let height = fun (_, _, _, _, h) -> h

您可能打算将description 元组传递给每个解构函数,如下所示:

let setArray (description: int * int * int * int * int)  =
    let id = description |> (fun (i, _, _, _, _) -> i)
    let startX = description |> (fun (_, x, _, _, _) -> x)
    let startY = description |> (fun (_, _, y, _, _) -> y)
    let width = description |> (fun (_, _, _, w, _) -> w)
    let height = description |> (fun (_, _, _, _, h) -> h)

但是有一种更简单的方法可以做到这一点。 F# 允许您在函数签名中解构元组。因此,您可以将整个代码块替换为以下内容:

let setArray (id, startX, startY, width, height) =

就是这样!所以现在你的整个函数看起来像:

let setArray (id, startX, startY, width, height) =
    let arrayResult = Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)
    arrayresult

您还可以进行另一种简化。任何时候你有let x = (some calculation) 紧跟x 作为函数的返回值,你可以去掉那个let 并让函数返回(some calculation)。应用这种简化,您的函数变为:

let setArray (id, startX, startY, width, height) =
    Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)

你就完成了!如果真的要指定idstartX等都是整数,可以这样:

let setArray (id : int, startX : int, startY : int, width : int, height : int) =
    Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)

【讨论】:

  • 感谢您提供如此完整的答案!我还在学习如何正确简化函数。
  • 还有一件事:我刚刚注意到这段代码仍然无法编译,因为if i &lt; width ... 表达式中没有else 分支。在 F# 中,假定没有 else 的任何 if 表达式末尾有 else ()。此外,所有if 表达式必须为真分支和假分支返回相同的类型;由于() 的类型unitint 不匹配,因此您的代码不会按原样编译。您需要添加else 0,以便if 变为if i &lt; width &amp;&amp; j &lt; height then id else 0
  • 是的,我也看到了,只是为网格添加了一个默认值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-24
  • 1970-01-01
  • 2023-03-28
  • 2011-12-29
  • 2021-07-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多