F#功能 - 预期类型与实际不同

时间:2013-03-02 05:02:11

标签: f# f#-3.0 f#-scripting

我一直在尝试编写一个curried函数“multvec”,它使用u =(u1,u2,...,un)和v =(v1,v2,...,vn)并输出u1 * v1 + u2 * v2 + ... + un * vn。我认为我的逻辑大多是正确的(至少它会在其他语言中......)但我一直得到:

stdin(11,57): error FS0001: This expression was expected to have type
'a list    
but here has type
'c list * 'd list -> 'b list

代码如下:问题显然在最后一行的产品调用中。但是,我的印象是基本情况(x * y):: []只会产生一个列表,而不是它实际生成的列表。

let rec multvec xs ys = function
    | [ ], [ ] -> failwith "Both lists cannot be empty"
    | x::[ ], y::[ ] -> ( x * y )::[ ]
    | x::xs, y::ys -> let product = multvec xs ys
                      ( x * y ) + ( List.reduce (+) product )

对此错误的任何明确性将不胜感激!先感谢您。

2 个答案:

答案 0 :(得分:8)

你的代码老实说它的错误比正确:; - ]

  • 这不是尾递归
  • functionxs是单独的参数时使用ys
  • 不完整模式匹配
  • 让一个分支评估到一个列表,另一个分支评估为标量
  • 所有产品都是手动添加的,除了最后一个产品,List.reduce在单个元素列表上运行 - 稍微向后; - ]

这是一个明智的实现,修复了以上所有内容:

let inline multvec xs ys = List.map2 (*) xs ys |> List.sum

请注意,如果性能是主要关注点,则可能值得避免使用List.sum,因为它使用了检查算术。如果使用未经检查的算术可以,则可以执行以下操作:

let inline multvec xs ys = List.map2 (*) xs ys |> List.reduce (+)

如果您真的想手动执行此操作,可以采用以下方法之一:

let inline multvec xs ys =
    let rec impl acc = function
        | [], []         -> acc
        | x::xs', y::ys' -> impl (x * y + acc) (xs', ys')
        | _              -> failwith "lists must be of equal length" 
    impl LanguagePrimitives.GenericZero (xs, ys)

答案 1 :(得分:1)

要添加到ildjarn的答案,您可以使用map2reducefold2融合到单个函数调用中,以计算两个向量的点积(“multvec”):

let inline dot xs ys =
    let zero = LanguagePrimitives.GenericZero
    List.fold2 (fun acc x y -> acc + x * y) zero xs ys

这样可以避免创建不必要的临时列表。