如何在 F# 中编写可变参数函数来模拟类似的 Haskell 解决方案?
How to write a variadic function in F# emulating a similar Haskell solution?
我如何(如果有的话)模拟可变参数函数(不是方法)以便我可以编写
sum 1 2 3
sum 1 2 3 4 5
sum 1 2 3 4 5 6 7
// etc.
上面的代码只是一个 示例 - 显然,如果我必须总结一个列表,那么
[ 1; 2 ; 3] |> List.sum
是一个更好的方法。
不过,我正在寻找 结构相似 的解决方案,例如 this Haskell solution
同样重要的是,函数调用和参数值的 正常 语法保持不变。所以
sum 1 2 3
对
sum(1, 2, 3)
这实际上意味着
let sum ([<ParamArray>] arr) = ...
在此特定情况下不需要。
所有这一切的动机:我正在探索 F# 的类型系统和语法的外围。而且我完全意识到我可能已经越过了可能的界限。
PS:我的具体想法(我没有在这里描述)也可以完全不同地解决——所以我知道,所以我已经做了。
因此,我的问题不是:如何以不同的方式解决这个问题,而是如何在结构上解决这个问题,例如 Haskell.
PPS:如果你能使整个解决方案递归,则加倍业力点。
如评论中所述,您可以在 F# 中使用 ParamArray
属性,这将使您可以使用多个参数调用该函数 - 尽管您必须使用 .NET 表示法并编写 sum(1,2,3,4,5,6)
.
也就是说,我可能不会在实践中这样做。如果您正在编写一个接受由未知数量的值组成的输入的函数,那么使用列表可能是更好的设计:
List.sum [1; 2; 3 ]
List.sum [1; 2; 3; 4; 5 ]
List.sum [1; 2; 3; 4; 5; 6; 7 ]
这只是几个字符,它可以更好地模拟您正在解决的问题 - 至少,基于您在此处发布的玩具示例。
如果不知道你实际解决的问题是什么,就很难给出一个好的答案。但总的来说,我认为采用列表是 F# 友好的默认设置。使用 ParamArray
在某些情况下以及对于 C# 互操作很有用。
你说的是函数,不是方法。所以 ParamArray
不是一个选项。
您链接的 Haskell 代码基于推断的结果类型。
下面是F#中根据推断结果类型解析的方法:
type T = T with
static member inline ($) (T, r:'t->'t ) = fun a b -> a + b
static member inline ($) (T, r:'t->'t->'t ) = fun a b c -> a + b + c
static member inline ($) (T, r:'t->'t->'t->'t) = fun a b c d -> a + b + c + d
let inline sum (x:'a) :'r = (T $ Unchecked.defaultof<'r>) x
let x:int = sum 2 3
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M
let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4 ) 10
let res3 = mult3Numbers 3 (sum 3 4 5) 10
更新
从 F# 4.1 开始,上述代码不再有效(请参阅评论),但这里有一个更好的示例,其中包含采用 n 个(无限)参数的递归多元函数:
type T = T with
static member ($) (T, _:int ) = (+)
static member ($) (T, _:decimal) = (+)
let inline sum (i:'a) (x:'a) :'r = (T $ Unchecked.defaultof<'r>) i x
type T with
static member inline ($) (T, _:'t-> 'rest) = fun (a:'t) -> (+) a >> sum
let x:int = sum 2 3
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M
let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4) (sum 2 2 3 3)
let res3 = mult3Numbers 3 (sum 3 4 5 11 13 20) 10
你也可以看看这个polyvariadic fold。
我如何(如果有的话)模拟可变参数函数(不是方法)以便我可以编写
sum 1 2 3
sum 1 2 3 4 5
sum 1 2 3 4 5 6 7
// etc.
上面的代码只是一个 示例 - 显然,如果我必须总结一个列表,那么
[ 1; 2 ; 3] |> List.sum
是一个更好的方法。
不过,我正在寻找 结构相似 的解决方案,例如 this Haskell solution
同样重要的是,函数调用和参数值的 正常 语法保持不变。所以
sum 1 2 3
对
sum(1, 2, 3)
这实际上意味着
let sum ([<ParamArray>] arr) = ...
在此特定情况下不需要。
所有这一切的动机:我正在探索 F# 的类型系统和语法的外围。而且我完全意识到我可能已经越过了可能的界限。
PS:我的具体想法(我没有在这里描述)也可以完全不同地解决——所以我知道,所以我已经做了。 因此,我的问题不是:如何以不同的方式解决这个问题,而是如何在结构上解决这个问题,例如 Haskell.
PPS:如果你能使整个解决方案递归,则加倍业力点。
如评论中所述,您可以在 F# 中使用 ParamArray
属性,这将使您可以使用多个参数调用该函数 - 尽管您必须使用 .NET 表示法并编写 sum(1,2,3,4,5,6)
.
也就是说,我可能不会在实践中这样做。如果您正在编写一个接受由未知数量的值组成的输入的函数,那么使用列表可能是更好的设计:
List.sum [1; 2; 3 ]
List.sum [1; 2; 3; 4; 5 ]
List.sum [1; 2; 3; 4; 5; 6; 7 ]
这只是几个字符,它可以更好地模拟您正在解决的问题 - 至少,基于您在此处发布的玩具示例。
如果不知道你实际解决的问题是什么,就很难给出一个好的答案。但总的来说,我认为采用列表是 F# 友好的默认设置。使用 ParamArray
在某些情况下以及对于 C# 互操作很有用。
你说的是函数,不是方法。所以 ParamArray
不是一个选项。
您链接的 Haskell 代码基于推断的结果类型。
下面是F#中根据推断结果类型解析的方法:
type T = T with
static member inline ($) (T, r:'t->'t ) = fun a b -> a + b
static member inline ($) (T, r:'t->'t->'t ) = fun a b c -> a + b + c
static member inline ($) (T, r:'t->'t->'t->'t) = fun a b c d -> a + b + c + d
let inline sum (x:'a) :'r = (T $ Unchecked.defaultof<'r>) x
let x:int = sum 2 3
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M
let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4 ) 10
let res3 = mult3Numbers 3 (sum 3 4 5) 10
更新
从 F# 4.1 开始,上述代码不再有效(请参阅评论),但这里有一个更好的示例,其中包含采用 n 个(无限)参数的递归多元函数:
type T = T with
static member ($) (T, _:int ) = (+)
static member ($) (T, _:decimal) = (+)
let inline sum (i:'a) (x:'a) :'r = (T $ Unchecked.defaultof<'r>) i x
type T with
static member inline ($) (T, _:'t-> 'rest) = fun (a:'t) -> (+) a >> sum
let x:int = sum 2 3
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M
let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4) (sum 2 2 3 3)
let res3 = mult3Numbers 3 (sum 3 4 5 11 13 20) 10
你也可以看看这个polyvariadic fold。